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 d1fd331cce..df40499a75 100644 --- a/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h +++ b/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h @@ -3249,4 +3249,42 @@ wlan_parse_oce_ap_tx_pwr_ie(uint8_t *mbo_oce_ie, int8_t *ap_tx_pwr_dbm) return false; } + +/** + * enum mlme_csa_event_ies_present_flag - IE present flag in CSA event + * @MLME_CSA_IE_PRESENT: CSA IE is present + * @MLME_XCSA_IE_PRESENT: extend CSA IE is present + * @MLME_WBW_IE_PRESENT: wide bandwidth channel switch IE is present + * @MLME_CSWRAP_IE_EXTENDED_PRESENT: channel switch wrapper IE is present + */ +enum mlme_csa_event_ies_present_flag { + MLME_CSA_IE_PRESENT = 0x00000001, + MLME_XCSA_IE_PRESENT = 0x00000002, + MLME_WBW_IE_PRESENT = 0x00000004, + MLME_CSWRAP_IE_EXTENDED_PRESENT = 0x00000008, +}; + +/** + * struct csa_offload_params - STA CSA offload request parameters + * @channel: channel + * @switch_mode: switch mode + * @sec_chan_offset: secondary channel offset + * @new_ch_width: new channel width + * @new_ch_freq_seg1: channel center freq 1 + * @new_ch_freq_seg2: channel center freq 2 + * @ies_present_flag: BIT MAP of MLME_CSA_EVENT_IES_PRESENT_FLAG + * @bssid: BSSID which triggers CSA + */ +struct csa_offload_params { + uint8_t channel; + uint32_t csa_chan_freq; + uint8_t switch_mode; + uint8_t sec_chan_offset; + uint8_t new_ch_width; + uint8_t new_op_class; + uint8_t new_ch_freq_seg1; + uint8_t new_ch_freq_seg2; + uint32_t ies_present_flag; + struct qdf_mac_addr bssid; +}; #endif /* _WLAN_CMN_IEEE80211_DEFS_H_ */ diff --git a/umac/mlme/vdev_mgr/core/src/vdev_mlme_sm.c b/umac/mlme/vdev_mgr/core/src/vdev_mlme_sm.c index ca5bfc6aad..de929407fa 100644 --- a/umac/mlme/vdev_mgr/core/src/vdev_mlme_sm.c +++ b/umac/mlme/vdev_mgr/core/src/vdev_mlme_sm.c @@ -1790,13 +1790,15 @@ static bool mlme_vdev_subst_up_active_event(void *ctx, uint16_t event, mlme_vdev_update_beacon(vdev_mlme, BEACON_INIT, event_data_len, event_data); if (mlme_vdev_up_send(vdev_mlme, event_data_len, - event_data) != QDF_STATUS_SUCCESS) + event_data) != QDF_STATUS_SUCCESS) { mlme_vdev_sm_deliver_event(vdev_mlme, WLAN_VDEV_SM_EV_UP_FAIL, event_data_len, event_data); - else + } else { mlme_vdev_notify_up_complete(vdev_mlme, event_data_len, event_data); + mlme_vdev_up_active_notify_mlo_mgr(vdev_mlme); + } status = true; break; diff --git a/umac/mlme/vdev_mgr/core/src/vdev_mlme_sm.h b/umac/mlme/vdev_mgr/core/src/vdev_mlme_sm.h index 174ca55dae..a0a68306ca 100644 --- a/umac/mlme/vdev_mgr/core/src/vdev_mlme_sm.h +++ b/umac/mlme/vdev_mgr/core/src/vdev_mlme_sm.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -25,6 +26,7 @@ #ifdef WLAN_FEATURE_11BE_MLO #include +#include #endif #include @@ -677,6 +679,20 @@ static inline void mlme_vdev_down_cmpl_notify_mlo_mgr( if (wlan_vdev_mlme_is_mlo_ap(vdev_mlme->vdev)) mlo_ap_link_down_cmpl_notify(vdev_mlme->vdev); } + +/** + * mlme_vdev_up_active_notify_mlo_mgr - notify mlo link is up active + * @vdev_mlme_obj: VDEV MLME comp object + * + * Return: VOID. + */ +static inline void mlme_vdev_up_active_notify_mlo_mgr( + struct vdev_mlme_obj *vdev_mlme) +{ + if ((wlan_vdev_mlme_get_opmode(vdev_mlme->vdev) == QDF_STA_MODE) && + wlan_vdev_mlme_is_mlo_vdev(vdev_mlme->vdev)) + mlo_sta_up_active_notify(vdev_mlme->vdev); +} #else static inline void mlme_vdev_up_notify_mlo_mgr(struct vdev_mlme_obj *vdev_mlme) { @@ -691,6 +707,11 @@ static inline void mlme_vdev_down_cmpl_notify_mlo_mgr( struct vdev_mlme_obj *vdev_mlme) { } + +static inline void mlme_vdev_up_active_notify_mlo_mgr( + struct vdev_mlme_obj *vdev_mlme) +{ +} #endif #ifdef VDEV_SM_LOCK_SUPPORT diff --git a/umac/mlo_mgr/inc/wlan_mlo_mgr_cmn.h b/umac/mlo_mgr/inc/wlan_mlo_mgr_cmn.h index 4de2837c46..30e656c486 100644 --- a/umac/mlo_mgr/inc/wlan_mlo_mgr_cmn.h +++ b/umac/mlo_mgr/inc/wlan_mlo_mgr_cmn.h @@ -277,6 +277,16 @@ void mlo_get_ml_vdev_list(struct wlan_objmgr_vdev *vdev, uint16_t *vdev_count, struct wlan_objmgr_vdev **wlan_vdev_list); +/** + * mlo_mlme_handle_sta_csa_param() - process saved mlo sta csa param + * @vdev: vdev pointer + * @csa_param: saved csa_param + * + * Return: None + */ +void mlo_mlme_handle_sta_csa_param(struct wlan_objmgr_vdev *vdev, + struct csa_offload_params *csa_param); + #define INVALID_HW_LINK_ID 0xFFFF #ifdef WLAN_MLO_MULTI_CHIP /** 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 7ecb5c506b..9b0d9d0ac6 100644 --- a/umac/mlo_mgr/inc/wlan_mlo_mgr_public_structs.h +++ b/umac/mlo_mgr/inc/wlan_mlo_mgr_public_structs.h @@ -176,6 +176,28 @@ struct wlan_mlo_key_mgmt { uint32_t gtk; }; +/** + * struct mlo_sta_csa _params - CSA request parameters in mlo mgr + * @csa_param: csa parameters + * @link_id: the link index of AP which triggers CSA + * @mlo_csa_synced: Before vdev is up, csa information is only saved but not + * handled, and this value is false. Once vdev is up, the saved + * csa information is handled, and this value is changed to + * true. Note this value will be true if the vdev is doing + * restart. + * @csa_offload_event_recvd: True if WMI_CSA_HANDLING_EVENTID is already + * received. False if this is the first + * WMI_CSA_HANDLING_EVENTID. + * @valid_csa_param: True once csa_param is filled. + */ +struct mlo_sta_csa_params { + struct csa_offload_params csa_param; + uint8_t link_id; + bool mlo_csa_synced; + bool csa_offload_event_recvd; + bool valid_csa_param; +}; + /* * struct mlo_sta_quiet_status - MLO sta quiet status * @link_id: link id @@ -196,6 +218,7 @@ struct mlo_sta_quiet_status { * @copied_conn_req: original connect req * @copied_conn_req_lock: lock for the original connect request * @assoc_rsp: Raw assoc response frame + * @mlo_csa_param: CSA request parameters for mlo sta */ struct wlan_mlo_sta { qdf_bitmap(wlan_connect_req_links, WLAN_UMAC_MLO_MAX_VDEVS); @@ -210,6 +233,7 @@ struct wlan_mlo_sta { #endif struct element_info assoc_rsp; struct mlo_sta_quiet_status mlo_quiet_status[WLAN_UMAC_MLO_MAX_VDEVS]; + struct mlo_sta_csa_params mlo_csa_param[WLAN_UMAC_MLO_MAX_VDEVS]; }; /* @@ -450,6 +474,7 @@ struct mlo_tgt_partner_info { * @mlo_mlme_ext_deauth: Callback to initiate deauth * @mlo_mlme_ext_clone_security_param: Callback to clone mlo security params * @mlo_mlme_ext_peer_process_auth: Callback to process pending auth + * @mlo_mlme_ext_handle_sta_csa_param: Callback to handle sta csa param */ struct mlo_mlme_ext_ops { QDF_STATUS (*mlo_mlme_ext_validate_conn_req)( @@ -474,6 +499,9 @@ struct mlo_mlme_ext_ops { void (*mlo_mlme_ext_peer_process_auth)( struct mlpeer_auth_params *auth_param); #endif + void (*mlo_mlme_ext_handle_sta_csa_param)( + struct wlan_objmgr_vdev *vdev, + struct csa_offload_params *csa_param); }; /* maximum size of vdev bitmap array for MLO link set active command */ diff --git a/umac/mlo_mgr/inc/wlan_mlo_mgr_sta.h b/umac/mlo_mgr/inc/wlan_mlo_mgr_sta.h index b868c50844..4dba43cd8f 100644 --- a/umac/mlo_mgr/inc/wlan_mlo_mgr_sta.h +++ b/umac/mlo_mgr/inc/wlan_mlo_mgr_sta.h @@ -431,6 +431,47 @@ bool mlo_is_sta_inactivity_allowed_with_quiet(struct wlan_objmgr_psoc *psoc, uint8_t num_mlo, uint8_t *mlo_idx, uint8_t affected_links, uint8_t *affected_list); + +/** + * mlo_is_sta_csa_synced - Is mlo sta csa parameters are synced or not + * + * @mlo_dev_ctx: mlo context + * @link_id: link id + * + * Return: true if mlo sta csa parameters of given link id is synced + */ +bool mlo_is_sta_csa_synced(struct wlan_mlo_dev_context *mlo_dev_ctx, + uint8_t link_id); + +/** + * mlo_sta_csa_save_params - Save csa parameters for mlo station + * @mlo_dev_ctx: mlo context + * @link_id: link id + * @csa_param: csa parameters to be saved + * + * Return: QDF_STATUS + */ +QDF_STATUS mlo_sta_csa_save_params(struct wlan_mlo_dev_context *mlo_dev_ctx, + uint8_t link_id, + struct csa_offload_params *csa_param); + +/** + * mlo_sta_up_active_notify - mlo sta up active notify + * @vdev: vdev obj mgr + * + * Return: QDF_STATUS + */ +QDF_STATUS mlo_sta_up_active_notify(struct wlan_objmgr_vdev *vdev); + +/** + * mlo_is_sta_csa_param_handled - Is given csa_param handled or not + * @vdev: vdev obj mgr + * @csa_param: csa parameters to be checked + * + * Return: true if given csa parameters is handled + */ +bool mlo_is_sta_csa_param_handled(struct wlan_objmgr_vdev *vdev, + struct csa_offload_params *csa_param); #else static inline QDF_STATUS mlo_connect(struct wlan_objmgr_vdev *vdev, @@ -546,5 +587,12 @@ mlo_get_assoc_rsp(struct wlan_objmgr_vdev *vdev, struct element_info *assoc_rsp_frame) { } + +static inline bool +mlo_is_sta_csa_param_handled(struct wlan_objmgr_vdev *vdev, + struct csa_offload_params *csa_param) +{ + return false; +} #endif #endif diff --git a/umac/mlo_mgr/src/wlan_mlo_mgr_cmn.c b/umac/mlo_mgr/src/wlan_mlo_mgr_cmn.c index 0a9dd44757..860371048d 100644 --- a/umac/mlo_mgr/src/wlan_mlo_mgr_cmn.c +++ b/umac/mlo_mgr/src/wlan_mlo_mgr_cmn.c @@ -585,3 +585,15 @@ out: return status; } + +void mlo_mlme_handle_sta_csa_param(struct wlan_objmgr_vdev *vdev, + struct csa_offload_params *csa_param) +{ + struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); + + if (!mlo_ctx || !mlo_ctx->mlme_ops || + !mlo_ctx->mlme_ops->mlo_mlme_ext_handle_sta_csa_param) + return; + + mlo_ctx->mlme_ops->mlo_mlme_ext_handle_sta_csa_param(vdev, csa_param); +} diff --git a/umac/mlo_mgr/src/wlan_mlo_mgr_sta.c b/umac/mlo_mgr/src/wlan_mlo_mgr_sta.c index e5d276f4f6..8d194f3c67 100644 --- a/umac/mlo_mgr/src/wlan_mlo_mgr_sta.c +++ b/umac/mlo_mgr/src/wlan_mlo_mgr_sta.c @@ -1303,4 +1303,231 @@ bool mlo_is_sta_inactivity_allowed_with_quiet(struct wlan_objmgr_psoc *psoc, return allowed; } + +bool mlo_is_sta_csa_synced(struct wlan_mlo_dev_context *mlo_dev_ctx, + uint8_t link_id) +{ + struct wlan_mlo_sta *sta_ctx; + int i; + bool sta_csa_synced = false; + + if (!mlo_dev_ctx) { + mlo_err("invalid mlo_dev_ctx"); + return sta_csa_synced; + } + + mlo_dev_lock_acquire(mlo_dev_ctx); + sta_ctx = mlo_dev_ctx->sta_ctx; + if (!sta_ctx) { + mlo_err("invalid sta_ctx"); + mlo_dev_lock_release(mlo_dev_ctx); + return sta_csa_synced; + } + for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param); i++) { + if (link_id == sta_ctx->mlo_csa_param[i].link_id && + (sta_ctx->mlo_csa_param[i].valid_csa_param || + sta_ctx->mlo_csa_param[i].mlo_csa_synced)) { + mlo_dev_lock_release(mlo_dev_ctx); + sta_csa_synced = + sta_ctx->mlo_csa_param[i].mlo_csa_synced; + break; + } + } + mlo_dev_lock_release(mlo_dev_ctx); + + return sta_csa_synced; +} + +QDF_STATUS mlo_sta_csa_save_params(struct wlan_mlo_dev_context *mlo_dev_ctx, + uint8_t link_id, + struct csa_offload_params *csa_param) +{ + struct wlan_mlo_sta *sta_ctx; + int i; + bool find_free_buffer = false; + int free_idx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!mlo_dev_ctx) { + mlo_err("invalid mlo_dev_ctx"); + status = QDF_STATUS_E_INVAL; + goto done; + } + + mlo_dev_lock_acquire(mlo_dev_ctx); + sta_ctx = mlo_dev_ctx->sta_ctx; + if (!sta_ctx) { + mlo_err("invalid sta_ctx"); + status = QDF_STATUS_E_INVAL; + goto rel_lock; + } + for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param); i++) { + if (!sta_ctx->mlo_csa_param[i].valid_csa_param && + !sta_ctx->mlo_csa_param[i].mlo_csa_synced) { + if (!find_free_buffer) { + free_idx = i; + find_free_buffer = true; + } + } else if (link_id == sta_ctx->mlo_csa_param[i].link_id) { + qdf_mem_copy(&sta_ctx->mlo_csa_param[i].csa_param, + csa_param, sizeof(*csa_param)); + mlo_debug("mld mac " QDF_MAC_ADDR_FMT " link id %d update csa", + QDF_MAC_ADDR_REF(mlo_dev_ctx->mld_addr.bytes), + link_id); + goto rel_lock; + } + } + if (!find_free_buffer) { + mlo_err("no free buffer of csa param for link %d in sta_ctx", + link_id); + status = QDF_STATUS_E_INVAL; + goto rel_lock; + } + qdf_mem_copy(&sta_ctx->mlo_csa_param[free_idx].csa_param, + csa_param, sizeof(*csa_param)); + sta_ctx->mlo_csa_param[free_idx].link_id = link_id; + sta_ctx->mlo_csa_param[free_idx].valid_csa_param = true; + mlo_debug("mld mac " QDF_MAC_ADDR_FMT " link id %d RX csa", + QDF_MAC_ADDR_REF(mlo_dev_ctx->mld_addr.bytes), + link_id); + +rel_lock: + mlo_dev_lock_release(mlo_dev_ctx); + +done: + + return status; +} + +QDF_STATUS mlo_sta_up_active_notify(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_mlo_sta *sta_ctx; + struct wlan_mlo_dev_context *mlo_dev_ctx; + uint8_t link_id; + int i; + bool find_free_buffer = false; + int free_idx; + struct csa_offload_params csa_param; + struct wlan_channel *chan; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!vdev) { + mlo_err("invalid vdev"); + status = QDF_STATUS_E_INVAL; + goto done; + } + link_id = wlan_vdev_get_link_id(vdev); + mlo_dev_ctx = vdev->mlo_dev_ctx; + if (!mlo_dev_ctx) { + mlo_err("invalid mlo_dev_ctx"); + status = QDF_STATUS_E_INVAL; + goto done; + } + mlo_dev_lock_acquire(mlo_dev_ctx); + sta_ctx = mlo_dev_ctx->sta_ctx; + if (!sta_ctx) { + mlo_err("invalid sta_ctx"); + status = QDF_STATUS_E_INVAL; + goto rel_lock; + } + + for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param); i++) { + if (!sta_ctx->mlo_csa_param[i].valid_csa_param && + !sta_ctx->mlo_csa_param[i].mlo_csa_synced) { + if (!find_free_buffer) { + free_idx = i; + find_free_buffer = true; + } + } else if (link_id == sta_ctx->mlo_csa_param[i].link_id) { + if (sta_ctx->mlo_csa_param[i].valid_csa_param && + !sta_ctx->mlo_csa_param[i].mlo_csa_synced) { + mlo_debug("mld mac " QDF_MAC_ADDR_FMT " vdev id %d link id %d handle csa", + QDF_MAC_ADDR_REF( + mlo_dev_ctx->mld_addr.bytes), + wlan_vdev_get_id(vdev), link_id); + csa_param = sta_ctx->mlo_csa_param[i].csa_param; + sta_ctx->mlo_csa_param[i].mlo_csa_synced = true; + mlo_dev_lock_release(mlo_dev_ctx); + chan = wlan_vdev_mlme_get_bss_chan(vdev); + if (csa_param.csa_chan_freq && chan && + csa_param.csa_chan_freq != chan->ch_freq) + mlo_mlme_handle_sta_csa_param( + vdev, &csa_param); + goto done; + } + sta_ctx->mlo_csa_param[i].mlo_csa_synced = true; + goto rel_lock; + } + } + if (!find_free_buffer) { + mlo_err("no free buffer of csa param for link %d in sta_ctx", + link_id); + goto rel_lock; + } + sta_ctx->mlo_csa_param[free_idx].mlo_csa_synced = true; + sta_ctx->mlo_csa_param[free_idx].link_id = link_id; + mlo_debug("mld mac " QDF_MAC_ADDR_FMT " link id %d UP Active", + QDF_MAC_ADDR_REF(mlo_dev_ctx->mld_addr.bytes), + link_id); + +rel_lock: + mlo_dev_lock_release(mlo_dev_ctx); + +done: + + return status; +} + +bool mlo_is_sta_csa_param_handled(struct wlan_objmgr_vdev *vdev, + struct csa_offload_params *csa_param) +{ + struct wlan_mlo_sta *sta_ctx; + struct wlan_mlo_dev_context *mlo_dev_ctx; + uint8_t link_id; + int i; + bool handled = false; + + if (!vdev) { + mlo_err("invalid vdev"); + goto done; + } + link_id = wlan_vdev_get_link_id(vdev); + mlo_dev_ctx = vdev->mlo_dev_ctx; + if (!mlo_dev_ctx) { + mlo_err("invalid mlo_dev_ctx"); + goto done; + } + mlo_dev_lock_acquire(mlo_dev_ctx); + sta_ctx = mlo_dev_ctx->sta_ctx; + if (!sta_ctx) { + mlo_err("invalid sta_ctx"); + goto rel_lock; + } + + for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param); i++) { + if (link_id == sta_ctx->mlo_csa_param[i].link_id && + (sta_ctx->mlo_csa_param[i].valid_csa_param || + sta_ctx->mlo_csa_param[i].mlo_csa_synced)) + break; + } + + if (i >= QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param)) { + mlo_debug("mlo csa synced does not happen before csa FW event"); + goto rel_lock; + } + if (!sta_ctx->mlo_csa_param[i].csa_offload_event_recvd) { + sta_ctx->mlo_csa_param[i].csa_offload_event_recvd = true; + if (sta_ctx->mlo_csa_param[i].valid_csa_param && + !qdf_mem_cmp(&sta_ctx->mlo_csa_param[i].csa_param, + csa_param, sizeof(*csa_param))) + handled = true; + } + +rel_lock: + mlo_dev_lock_release(mlo_dev_ctx); + +done: + + return handled; +} #endif