|
@@ -18,8 +18,10 @@
|
|
* DOC: contains MLO manager Link Switch related functionality
|
|
* DOC: contains MLO manager Link Switch related functionality
|
|
*/
|
|
*/
|
|
#include <wlan_mlo_mgr_link_switch.h>
|
|
#include <wlan_mlo_mgr_link_switch.h>
|
|
-#include <wlan_objmgr_vdev_obj.h>
|
|
|
|
-#include <wlan_mlo_mgr_cmn.h>
|
|
|
|
|
|
+#include <wlan_mlo_mgr_main.h>
|
|
|
|
+#include <wlan_mlo_mgr_sta.h>
|
|
|
|
+#include <wlan_serialization_api.h>
|
|
|
|
+#include <wlan_cm_api.h>
|
|
|
|
|
|
void mlo_mgr_update_link_info_mac_addr(struct wlan_objmgr_vdev *vdev,
|
|
void mlo_mgr_update_link_info_mac_addr(struct wlan_objmgr_vdev *vdev,
|
|
struct wlan_mlo_link_mac_update *ml_mac_update)
|
|
struct wlan_mlo_link_mac_update *ml_mac_update)
|
|
@@ -214,6 +216,77 @@ void mlo_mgr_free_link_info_wmi_chan(struct wlan_mlo_dev_context *ml_dev)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
|
|
|
|
+bool mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
|
|
|
|
+{
|
|
|
|
+ enum mlo_link_switch_req_state state;
|
|
|
|
+ struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
|
|
|
|
+
|
|
|
|
+ if (!mlo_dev_ctx)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ state = mlo_mgr_link_switch_get_curr_state(mlo_dev_ctx);
|
|
|
|
+ return (state != MLO_LINK_SWITCH_STATE_IDLE);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev *vdev)
|
|
|
|
+{
|
|
|
|
+ if (!mlo_mgr_is_link_switch_in_progress(vdev))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ return vdev->mlo_dev_ctx->link_ctx->last_req.restore_vdev_flag;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void mlo_mgr_link_switch_init_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
|
|
|
|
+{
|
|
|
|
+ mlo_dev_lock_acquire(mlo_dev_ctx);
|
|
|
|
+ mlo_dev_ctx->link_ctx->last_req.state = MLO_LINK_SWITCH_STATE_IDLE;
|
|
|
|
+ mlo_dev_lock_release(mlo_dev_ctx);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+mlo_mgr_link_switch_trans_next_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
|
|
|
|
+{
|
|
|
|
+ enum mlo_link_switch_req_state cur_state, next_state;
|
|
|
|
+
|
|
|
|
+ mlo_dev_lock_acquire(mlo_dev_ctx);
|
|
|
|
+ cur_state = mlo_dev_ctx->link_ctx->last_req.state;
|
|
|
|
+ switch (cur_state) {
|
|
|
|
+ case MLO_LINK_SWITCH_STATE_IDLE:
|
|
|
|
+ next_state = MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK;
|
|
|
|
+ break;
|
|
|
|
+ case MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK:
|
|
|
|
+ next_state = MLO_LINK_SWITCH_STATE_SET_MAC_ADDR;
|
|
|
|
+ break;
|
|
|
|
+ case MLO_LINK_SWITCH_STATE_SET_MAC_ADDR:
|
|
|
|
+ next_state = MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK;
|
|
|
|
+ break;
|
|
|
|
+ case MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK:
|
|
|
|
+ next_state = MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS;
|
|
|
|
+ break;
|
|
|
|
+ case MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS:
|
|
|
|
+ next_state = MLO_LINK_SWITCH_STATE_IDLE;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ QDF_ASSERT(0);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ mlo_dev_ctx->link_ctx->last_req.state = next_state;
|
|
|
|
+ mlo_dev_lock_release(mlo_dev_ctx);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+enum mlo_link_switch_req_state
|
|
|
|
+mlo_mgr_link_switch_get_curr_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
|
|
|
|
+{
|
|
|
|
+ enum mlo_link_switch_req_state state;
|
|
|
|
+
|
|
|
|
+ mlo_dev_lock_acquire(mlo_dev_ctx);
|
|
|
|
+ state = mlo_dev_ctx->link_ctx->last_req.state;
|
|
|
|
+ mlo_dev_lock_release(mlo_dev_ctx);
|
|
|
|
+
|
|
|
|
+ return state;
|
|
|
|
+}
|
|
|
|
+
|
|
QDF_STATUS mlo_mgr_link_switch_init(struct wlan_mlo_dev_context *ml_dev)
|
|
QDF_STATUS mlo_mgr_link_switch_init(struct wlan_mlo_dev_context *ml_dev)
|
|
{
|
|
{
|
|
ml_dev->link_ctx =
|
|
ml_dev->link_ctx =
|
|
@@ -222,6 +295,7 @@ QDF_STATUS mlo_mgr_link_switch_init(struct wlan_mlo_dev_context *ml_dev)
|
|
if (!ml_dev->link_ctx)
|
|
if (!ml_dev->link_ctx)
|
|
return QDF_STATUS_E_NOMEM;
|
|
return QDF_STATUS_E_NOMEM;
|
|
|
|
|
|
|
|
+ mlo_mgr_link_switch_init_state(ml_dev);
|
|
mlo_mgr_alloc_link_info_wmi_chan(ml_dev);
|
|
mlo_mgr_alloc_link_info_wmi_chan(ml_dev);
|
|
mlo_mgr_update_link_info_reset(ml_dev);
|
|
mlo_mgr_update_link_info_reset(ml_dev);
|
|
|
|
|
|
@@ -236,7 +310,6 @@ QDF_STATUS mlo_mgr_link_switch_deinit(struct wlan_mlo_dev_context *ml_dev)
|
|
return QDF_STATUS_SUCCESS;
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
-#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
|
|
|
|
void
|
|
void
|
|
mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev, int32_t link_id)
|
|
mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev, int32_t link_id)
|
|
{
|
|
{
|
|
@@ -263,4 +336,270 @@ mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev, int32_t link_id)
|
|
osif_bss_update_cb(&link_info->link_addr, &link_info->ap_link_addr,
|
|
osif_bss_update_cb(&link_info->link_addr, &link_info->ap_link_addr,
|
|
link_id);
|
|
link_id);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+static QDF_STATUS
|
|
|
|
+mlo_mgr_start_link_switch(struct wlan_objmgr_vdev *vdev,
|
|
|
|
+ struct wlan_serialization_command *cmd)
|
|
|
|
+{
|
|
|
|
+ uint8_t vdev_id, old_link_id, new_link_id;
|
|
|
|
+ struct mlo_link_switch_context *link_ctx = vdev->mlo_dev_ctx->link_ctx;
|
|
|
|
+ struct wlan_mlo_link_switch_req *req = &link_ctx->last_req;
|
|
|
|
+
|
|
|
|
+ vdev_id = wlan_vdev_get_id(vdev);
|
|
|
|
+ old_link_id = req->curr_ieee_link_id;
|
|
|
|
+ new_link_id = req->new_ieee_link_id;
|
|
|
|
+
|
|
|
|
+ mlo_debug("VDEV %d start link switch", vdev_id);
|
|
|
|
+ if (!wlan_cm_is_vdev_connected(vdev) ||
|
|
|
|
+ wlan_vdev_get_link_id(vdev) != old_link_id) {
|
|
|
|
+ mlo_err("Link switch req link id mismatch, curr link id %d",
|
|
|
|
+ wlan_vdev_get_link_id(vdev));
|
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ wlan_vdev_mlme_set_mlo_link_switch_in_progress(vdev);
|
|
|
|
+ if (!wlan_vdev_mlme_is_mlo_link_vdev(vdev)) {
|
|
|
|
+ wlan_vdev_mlme_set_mlo_link_vdev(vdev);
|
|
|
|
+ req->restore_vdev_flag = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mlo_mgr_remove_link_switch_cmd(vdev);
|
|
|
|
+
|
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static QDF_STATUS
|
|
|
|
+mlo_mgr_ser_link_switch_cb(struct wlan_serialization_command *cmd,
|
|
|
|
+ enum wlan_serialization_cb_reason reason)
|
|
|
|
+{
|
|
|
|
+ struct wlan_objmgr_vdev *vdev;
|
|
|
|
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
|
|
|
|
+
|
|
|
|
+ if (!cmd) {
|
|
|
|
+ mlo_err("cmd is NULL, reason: %d", reason);
|
|
|
|
+ QDF_ASSERT(0);
|
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ vdev = cmd->vdev;
|
|
|
|
+ switch (reason) {
|
|
|
|
+ case WLAN_SER_CB_ACTIVATE_CMD:
|
|
|
|
+ status = mlo_mgr_start_link_switch(vdev, cmd);
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_SER_CB_RELEASE_MEM_CMD:
|
|
|
|
+ mlo_mgr_link_switch_complete(vdev);
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_SER_CB_CANCEL_CMD:
|
|
|
|
+ mlo_err("Link switch cmd cancelled");
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
|
|
|
|
+ mlo_err("Link switch active cmd timeout");
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ QDF_ASSERT(0);
|
|
|
|
+ mlo_mgr_link_switch_complete(vdev);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return status;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void mlo_mgr_remove_link_switch_cmd(struct wlan_objmgr_vdev *vdev)
|
|
|
|
+{
|
|
|
|
+ struct wlan_serialization_queued_cmd_info cmd_info;
|
|
|
|
+ enum mlo_link_switch_req_state cur_state;
|
|
|
|
+ uint8_t vdev_id = wlan_vdev_get_id(vdev);
|
|
|
|
+ struct wlan_mlo_link_switch_req *req;
|
|
|
|
+
|
|
|
|
+ req = &vdev->mlo_dev_ctx->link_ctx->last_req;
|
|
|
|
+ if (req->restore_vdev_flag) {
|
|
|
|
+ wlan_vdev_mlme_clear_mlo_link_vdev(vdev);
|
|
|
|
+ req->restore_vdev_flag = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cur_state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
|
|
|
|
+ if (cur_state == MLO_LINK_SWITCH_STATE_IDLE)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ cmd_info.cmd_id = (vdev_id << 16) + (req->new_ieee_link_id << 8) +
|
|
|
|
+ (req->curr_ieee_link_id);
|
|
|
|
+ cmd_info.req_type = WLAN_SER_CANCEL_NON_SCAN_CMD;
|
|
|
|
+ cmd_info.cmd_type = WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH;
|
|
|
|
+ cmd_info.vdev = vdev;
|
|
|
|
+ cmd_info.queue_type = WLAN_SERIALIZATION_ACTIVE_QUEUE;
|
|
|
|
+
|
|
|
|
+ wlan_serialization_remove_cmd(&cmd_info);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#define MLO_MGR_MAX_LSWITCH_TIMEOUT 35000
|
|
|
|
+
|
|
|
|
+QDF_STATUS mlo_mgr_ser_link_switch_cmd(struct wlan_objmgr_vdev *vdev,
|
|
|
|
+ struct wlan_mlo_link_switch_req *req)
|
|
|
|
+{
|
|
|
|
+ enum wlan_serialization_status ser_cmd_status;
|
|
|
|
+ struct wlan_serialization_command cmd = {0};
|
|
|
|
+ uint8_t vdev_id = wlan_vdev_get_id(vdev);
|
|
|
|
+ struct mlo_link_switch_context *link_ctx;
|
|
|
|
+
|
|
|
|
+ if (!vdev->mlo_dev_ctx) {
|
|
|
|
+ mlo_err("ML dev ctx NULL, reject link switch");
|
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ link_ctx = vdev->mlo_dev_ctx->link_ctx;
|
|
|
|
+ link_ctx->last_req = *req;
|
|
|
|
+
|
|
|
|
+ cmd.cmd_type = WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH;
|
|
|
|
+ cmd.cmd_id = (vdev_id << 16) + (req->new_ieee_link_id << 8) +
|
|
|
|
+ (req->curr_ieee_link_id);
|
|
|
|
+ cmd.cmd_cb = mlo_mgr_ser_link_switch_cb;
|
|
|
|
+ cmd.source = WLAN_UMAC_COMP_MLO_MGR;
|
|
|
|
+ cmd.is_high_priority = false;
|
|
|
|
+ cmd.cmd_timeout_duration = MLO_MGR_MAX_LSWITCH_TIMEOUT;
|
|
|
|
+ cmd.vdev = vdev;
|
|
|
|
+ cmd.is_blocking = true;
|
|
|
|
+
|
|
|
|
+ ser_cmd_status = wlan_serialization_request(&cmd);
|
|
|
|
+ switch (ser_cmd_status) {
|
|
|
|
+ case WLAN_SER_CMD_PENDING:
|
|
|
|
+ mlo_debug("Link switch cmd in pending queue");
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_SER_CMD_ACTIVE:
|
|
|
|
+ mlo_debug("Link switch cmd in active queue");
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+QDF_STATUS
|
|
|
|
+mlo_mgr_link_switch_validate_request(struct wlan_objmgr_vdev *vdev,
|
|
|
|
+ struct wlan_mlo_link_switch_req *req)
|
|
|
|
+{
|
|
|
|
+ QDF_STATUS status = QDF_STATUS_E_INVAL;
|
|
|
|
+ uint8_t vdev_id = wlan_vdev_get_id(vdev);
|
|
|
|
+
|
|
|
|
+ if (req->curr_ieee_link_id >= WLAN_INVALID_LINK_ID ||
|
|
|
|
+ req->new_ieee_link_id >= WLAN_INVALID_LINK_ID) {
|
|
|
|
+ mlo_err("Invalid link params, curr link id %d, new link id %d",
|
|
|
|
+ req->curr_ieee_link_id, req->new_ieee_link_id);
|
|
|
|
+ return status;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!mlo_mgr_get_ap_link_by_link_id(vdev, req->new_ieee_link_id)) {
|
|
|
|
+ mlo_err("New link id %d not part of association",
|
|
|
|
+ req->new_ieee_link_id);
|
|
|
|
+ return status;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!mlo_is_mld_sta(vdev)) {
|
|
|
|
+ mlo_err("Link switch req not valid for VDEV %d", vdev_id);
|
|
|
|
+ return status;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!wlan_cm_is_vdev_connected(vdev)) {
|
|
|
|
+ mlo_err("VDEV %d not in connected state", vdev_id);
|
|
|
|
+ return status;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (mlo_mgr_is_link_switch_in_progress(vdev)) {
|
|
|
|
+ mlo_err("Link switch already in progress");
|
|
|
|
+ return status;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (wlan_vdev_get_link_id(vdev) != req->curr_ieee_link_id) {
|
|
|
|
+ mlo_err("VDEV %d link id wrong, curr link id %d",
|
|
|
|
+ vdev_id, wlan_vdev_get_link_id(vdev));
|
|
|
|
+ return status;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+QDF_STATUS mlo_mgr_link_switch_request_params(struct wlan_objmgr_psoc *psoc,
|
|
|
|
+ void *evt_params)
|
|
|
|
+{
|
|
|
|
+ QDF_STATUS status;
|
|
|
|
+ struct wlan_mlo_link_switch_req *req;
|
|
|
|
+ struct wlan_objmgr_vdev *vdev;
|
|
|
|
+
|
|
|
|
+ if (!evt_params) {
|
|
|
|
+ mlo_err("Invalid params");
|
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ req = (struct wlan_mlo_link_switch_req *)evt_params;
|
|
|
|
+
|
|
|
|
+ /* The reference is released on Link Switch status confirm to FW */
|
|
|
|
+ vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, req->vdev_id,
|
|
|
|
+ WLAN_MLO_MGR_ID);
|
|
|
|
+ if (!vdev) {
|
|
|
|
+ mlo_err("Invalid VDEV for link switch");
|
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ status = mlo_mgr_link_switch_validate_request(vdev, req);
|
|
|
|
+ if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
|
+ wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID);
|
|
|
|
+ mlo_debug("Link switch params/request invalid");
|
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mlo_debug("VDEV %d, curr_link_id %d, new_link_id %d, new_freq %d, new_phymode: %d, reason %d",
|
|
|
|
+ req->vdev_id, req->curr_ieee_link_id, req->new_ieee_link_id,
|
|
|
|
+ req->new_primary_freq, req->new_phymode, req->reason);
|
|
|
|
+
|
|
|
|
+ status = mlo_mgr_ser_link_switch_cmd(vdev, req);
|
|
|
|
+ if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
|
+ /* Release ref as link switch is not serialized */
|
|
|
|
+ wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID);
|
|
|
|
+ mlo_err("Failed to serialize link switch command");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return status;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+QDF_STATUS mlo_mgr_link_switch_complete(struct wlan_objmgr_vdev *vdev)
|
|
|
|
+{
|
|
|
|
+ QDF_STATUS status;
|
|
|
|
+ enum mlo_link_switch_req_state state;
|
|
|
|
+ struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops;
|
|
|
|
+ struct wlan_mlo_link_switch_cnf params = {0};
|
|
|
|
+ struct mlo_link_switch_context *link_ctx;
|
|
|
|
+ struct wlan_mlo_link_switch_req *req;
|
|
|
|
+ struct wlan_objmgr_psoc *psoc;
|
|
|
|
+
|
|
|
|
+ /* Not checking NULL value as reference is already taken for vdev */
|
|
|
|
+ psoc = wlan_vdev_get_psoc(vdev);
|
|
|
|
+
|
|
|
|
+ link_ctx = vdev->mlo_dev_ctx->link_ctx;
|
|
|
|
+ req = &link_ctx->last_req;
|
|
|
|
+
|
|
|
|
+ mlo_tx_ops = &psoc->soc_cb.tx_ops->mlo_ops;
|
|
|
|
+ if (!mlo_tx_ops || !mlo_tx_ops->send_mlo_link_switch_cnf_cmd) {
|
|
|
|
+ mlo_err("handler is not registered");
|
|
|
|
+ status = QDF_STATUS_E_NULL_VALUE;
|
|
|
|
+ goto release_ref;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
|
|
|
|
+ if (state != MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS)
|
|
|
|
+ params.status = MLO_LINK_SWITCH_CNF_STATUS_REJECT;
|
|
|
|
+ else
|
|
|
|
+ params.status = MLO_LINK_SWITCH_CNF_STATUS_ACCEPT;
|
|
|
|
+
|
|
|
|
+ params.vdev_id = wlan_vdev_get_id(vdev);
|
|
|
|
+ params.reason = MLO_LINK_SWITCH_CNF_REASON_BSS_PARAMS_CHANGED;
|
|
|
|
+
|
|
|
|
+ status = mlo_tx_ops->send_mlo_link_switch_cnf_cmd(psoc, ¶ms);
|
|
|
|
+ mlo_debug("VDEV %d link switch completed", params.vdev_id);
|
|
|
|
+
|
|
|
|
+release_ref:
|
|
|
|
+ mlo_mgr_link_switch_init_state(vdev->mlo_dev_ctx);
|
|
|
|
+ wlan_vdev_mlme_clear_mlo_link_switch_in_progress(vdev);
|
|
|
|
+ wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID);
|
|
|
|
+ return status;
|
|
|
|
+}
|
|
#endif
|
|
#endif
|