diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c b/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c index 8390729423..9d1a522533 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c @@ -584,6 +584,22 @@ static void cm_create_bss_peer(struct cnx_mgr *cm_ctx, } } +static +QDF_STATUS cm_if_mgr_validate_candidate(struct cnx_mgr *cm_ctx, + struct scan_cache_entry *scan_entry) +{ + struct if_mgr_event_data event_data = {0}; + + event_data.validate_bss_info.chan_freq = scan_entry->channel.chan_freq; + event_data.validate_bss_info.beacon_interval = scan_entry->bcn_int; + qdf_copy_macaddr(&event_data.validate_bss_info.peer_addr, + &scan_entry->bssid); + + return if_mgr_deliver_event(cm_ctx->vdev, + WLAN_IF_MGR_EV_VALIDATE_CANDIDATE, + &event_data); +} + #ifdef CONN_MGR_ADV_FEATURE #ifdef WLAN_FEATURE_FILS_SK /* @@ -678,6 +694,65 @@ static inline QDF_STATUS cm_set_fils_key(struct cnx_mgr *cm_ctx, } #endif /* WLAN_FEATURE_FILS_SK */ +static void cm_get_vdev_id_from_bssid(struct wlan_objmgr_pdev *pdev, + void *object, void *arg) +{ + uint8_t *vdev_id = arg; + struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)object; + + if (!vdev_id) + return; + + if (!(wlan_vdev_mlme_get_opmode(vdev) == QDF_STA_MODE || + wlan_vdev_mlme_get_opmode(vdev) == QDF_P2P_CLIENT_MODE)) + return; + + if (cm_is_vdev_disconnecting(vdev)) + *vdev_id = wlan_vdev_get_id(vdev); +} + +/** + * cm_is_any_other_vdev_disconnecting() - check whether any other vdev is in + * disconnecting state + * @cm_ctx: connection manager context + * @cm_req: Connect request. + * + * As Connect is a blocking call this API will make sure the disconnect + * doesnt timeout on any vdev and thus make sure that PEER/VDEV SM are cleaned + * before vdev delete is sent. + * + * Return : true if disconnection is on any vdev_id + */ +static bool cm_is_any_other_vdev_disconnecting(struct cnx_mgr *cm_ctx, + struct cm_req *cm_req) +{ + struct wlan_objmgr_pdev *pdev; + uint8_t vdev_id; + uint8_t cur_vdev_id = wlan_vdev_get_id(cm_ctx->vdev); + + pdev = wlan_vdev_get_pdev(cm_ctx->vdev); + if (!pdev) { + mlme_err(CM_PREFIX_FMT "Failed to find pdev", + CM_PREFIX_REF(cur_vdev_id, cm_req->cm_id)); + return false; + } + + vdev_id = WLAN_INVALID_VDEV_ID; + wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP, + cm_get_vdev_id_from_bssid, + &vdev_id, 0, + WLAN_MLME_CM_ID); + + if (vdev_id != WLAN_INVALID_VDEV_ID && vdev_id != cur_vdev_id) { + mlme_info(CM_PREFIX_FMT "Abort connection as vdev %d is waiting for disconnect", + CM_PREFIX_REF(cur_vdev_id, cm_req->cm_id), + vdev_id); + return true; + } + + return false; +} + QDF_STATUS cm_inform_blm_connect_complete(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *resp) @@ -717,6 +792,7 @@ static bool cm_is_retry_with_same_candidate(struct cnx_mgr *cm_ctx, uint32_t key_mgmt; struct wlan_objmgr_psoc *psoc; bool sae_connection; + QDF_STATUS status; psoc = wlan_pdev_get_psoc(wlan_vdev_get_pdev(cm_ctx->vdev)); key_mgmt = req->cur_candidate->entry->neg_sec_info.key_mgmt; @@ -754,6 +830,11 @@ use_same_candidate: if (req->cur_candidate_retries >= max_retry_count) return false; + status = cm_if_mgr_validate_candidate(cm_ctx, + req->cur_candidate->entry); + if (QDF_IS_STATUS_ERROR(status)) + return false; + mlme_info(CM_PREFIX_FMT "Retry again with " QDF_MAC_ADDR_FMT ", status code %d reason %d key_mgmt 0x%x retry count %d max retry %d", CM_PREFIX_REF(resp->vdev_id, resp->cm_id), QDF_MAC_ADDR_REF(resp->bssid.bytes), resp->status_code, @@ -876,6 +957,13 @@ static void cm_set_fils_wep_key(struct cnx_mgr *cm_ctx, } #else +static inline +bool cm_is_any_other_vdev_disconnecting(struct cnx_mgr *cm_ctx, + struct cm_req *cm_req) +{ + return false; +} + static inline bool cm_is_retry_with_same_candidate(struct cnx_mgr *cm_ctx, struct cm_connect_req *req, @@ -1148,22 +1236,6 @@ static QDF_STATUS cm_connect_get_candidates(struct wlan_objmgr_pdev *pdev, return QDF_STATUS_SUCCESS; } -static -QDF_STATUS cm_if_mgr_validate_candidate(struct cnx_mgr *cm_ctx, - struct scan_cache_entry *scan_entry) -{ - struct if_mgr_event_data event_data = {0}; - - event_data.validate_bss_info.chan_freq = scan_entry->channel.chan_freq; - event_data.validate_bss_info.beacon_interval = scan_entry->bcn_int; - qdf_copy_macaddr(&event_data.validate_bss_info.peer_addr, - &scan_entry->bssid); - - return if_mgr_deliver_event(cm_ctx->vdev, - WLAN_IF_MGR_EV_VALIDATE_CANDIDATE, - &event_data); -} - QDF_STATUS cm_if_mgr_inform_connect_complete(struct wlan_objmgr_vdev *vdev, QDF_STATUS connect_status) { @@ -1375,6 +1447,26 @@ static QDF_STATUS cm_get_valid_candidate(struct cnx_mgr *cm_ctx, } prev_candidate = cm_req->connect_req.cur_candidate; + /* + * In case of STA/CLI + STA/CLI, if a STA/CLI is in connecting state and + * a disconnect is received on any other STA/CLI, the disconnect can + * timeout waiting for the connection on first STA/CLI to get completed. + * This is because the connect is a blocking serialization command and + * it can try multiple candidates and thus can take upto 30+ sec to + * complete. + * + * Now osif will proceed with vdev delete after disconnect timeout. + * This can lead to vdev delete sent without vdev down/stop/peer delete + * for the vdev. + * + * So abort the connection if any of the vdev is waiting for disconnect, + * to avoid disconnect timeout. + */ + if (cm_is_any_other_vdev_disconnecting(cm_ctx, cm_req)) { + status = QDF_STATUS_E_FAILURE; + goto flush_single_pmk; + } + if (cm_req->connect_req.connect_attempts >= cm_ctx->max_connect_attempts) { mlme_info(CM_PREFIX_FMT "%d attempts tried, max %d",