diff --git a/os_if/linux/mlme/src/osif_cm_connect_rsp.c b/os_if/linux/mlme/src/osif_cm_connect_rsp.c index 4e4ed283e3..894d57d3c8 100644 --- a/os_if/linux/mlme/src/osif_cm_connect_rsp.c +++ b/os_if/linux/mlme/src/osif_cm_connect_rsp.c @@ -1080,13 +1080,14 @@ QDF_STATUS osif_connect_handler(struct wlan_objmgr_vdev *vdev, struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev); QDF_STATUS status; - osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " Connect with " QDF_MAC_ADDR_FMT " SSID \"%.*s\" is %s cm_id 0x%x cm_reason %d status_code %d is_reassoc %d", + osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " Connect with " QDF_MAC_ADDR_FMT " SSID \"%.*s\" is %s cm_id 0x%x cm_reason %d status_code %d is_reassoc %d send discon %d", osif_priv->wdev->netdev->name, rsp->vdev_id, QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)), QDF_MAC_ADDR_REF(rsp->bssid.bytes), rsp->ssid.length, rsp->ssid.ssid, rsp->connect_status ? "FAILURE" : "SUCCESS", rsp->cm_id, - rsp->reason, rsp->status_code, rsp->is_reassoc); + rsp->reason, rsp->status_code, rsp->is_reassoc, + rsp->send_disconnect); osif_check_and_unlink_bss(vdev, osif_priv, rsp); @@ -1097,8 +1098,34 @@ QDF_STATUS osif_connect_handler(struct wlan_objmgr_vdev *vdev, } osif_cm_connect_comp_ind(vdev, rsp, OSIF_PRE_USERSPACE_UPDATE); + + /* + * To fix issue that scan with random address failed since wdev keeps + * connected, rsp->send_disconnect is added. + * Reproduce steps: + * 1. Connect from OSIF success, wdev->connected = true; + * 2. Disconnect from target if and reassoc from OSIF happens back to + * back. + * 3. Disconnect event is not sent to kernel, wdev->connected keeps + * true, isn't cleared. + * 4. Connect from OSIF failed too, wdev->connected keeps true, isn't + * cleared. + * 5. Scan with random address failed since wdev->connected is true. + * + * To fix it, if connect req was a reassoc req and received in not + * connected state for race between disconnect from target if and + * reassoc connect from OSIF, set reassoc_in_non_connected to send + * disconnect instead of connect rsp to kernel to cleanup kernel flags + * like: wdev->connected. + */ if (rsp->is_reassoc) osif_indicate_reassoc_results(vdev, osif_priv, rsp); + else if (rsp->send_disconnect && + QDF_IS_STATUS_ERROR(rsp->connect_status)) + osif_cm_indicate_disconnect(vdev, osif_priv->wdev->netdev, + WLAN_REASON_UNSPECIFIED, + false, NULL, 0, + qdf_mem_malloc_flags()); else osif_indcate_connect_results(vdev, osif_priv, rsp); osif_cm_connect_comp_ind(vdev, rsp, OSIF_POST_USERSPACE_UPDATE); diff --git a/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c b/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c index 6999dbca42..8db35526d2 100644 --- a/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c +++ b/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2015, 2020-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 above @@ -81,7 +82,7 @@ rel_lock: (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)) #ifdef WLAN_FEATURE_11BE_MLO #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE -static void +void osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev, struct net_device *dev, enum ieee80211_reasoncode reason, @@ -98,7 +99,7 @@ osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev, } } #else /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */ -static void +void osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev, struct net_device *dev, enum ieee80211_reasoncode reason, @@ -128,7 +129,7 @@ osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev, } #endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */ #else /* WLAN_FEATURE_11BE_MLO */ -static void +void osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev, struct net_device *dev, enum ieee80211_reasoncode reason, @@ -139,7 +140,7 @@ osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev, } #endif /* WLAN_FEATURE_11BE_MLO */ #else -static void +void osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev, struct net_device *dev, enum ieee80211_reasoncode reason, diff --git a/os_if/linux/mlme/src/osif_cm_rsp.h b/os_if/linux/mlme/src/osif_cm_rsp.h index 02c4d84d1d..fe8c664e2e 100644 --- a/os_if/linux/mlme/src/osif_cm_rsp.h +++ b/os_if/linux/mlme/src/osif_cm_rsp.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2015,2020-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 above @@ -134,4 +135,25 @@ osif_indicate_reassoc_results(struct wlan_objmgr_vdev *vdev, QDF_STATUS osif_failed_candidate_handler(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *rsp); +/** + * osif_cm_indicate_disconnect - notify osif that connection was dropped + * + * @vdev: pointer to vdev + * @dev: network device + * @reason: reason code for the disconnection, set it to 0 if unknown + * @locally_generated: disconnection was requested locally + * @ie: information elements of the deauth/disassoc frame (may be %NULL) + * @ie_len: length of IEs + * @gfp: allocation flags + * + * After it calls this function, the driver should enter an idle state + * and not try to connect to any AP any more. + */ +void +osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev, + struct net_device *dev, + enum ieee80211_reasoncode reason, + bool locally_generated, const u8 *ie, + size_t ie_len, gfp_t gfp); + #endif /* __OSIF_CM_RSP_H */ 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 84f3e702cd..0ac3f8325c 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c @@ -1340,6 +1340,15 @@ cm_handle_connect_req_in_non_init_state(struct cnx_mgr *cm_ctx, struct cm_connect_req *cm_req, enum wlan_cm_sm_state cm_state_substate) { + if (cm_state_substate != WLAN_CM_S_CONNECTED && + cm_is_connect_req_reassoc(&cm_req->req)) { + cm_req->req.reassoc_in_non_connected = true; + mlme_debug(CM_PREFIX_FMT "Reassoc received in %d state", + CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), + cm_req->cm_id), + cm_state_substate); + } + switch (cm_state_substate) { case WLAN_CM_S_ROAMING: /* for FW roam/LFR3 remove the req from the list */ @@ -2236,11 +2245,69 @@ cm_clear_vdev_mlo_cap(struct wlan_objmgr_vdev *vdev) { } #endif /*WLAN_FEATURE_11BE_MLO*/ +/** + * cm_is_connect_id_reassoc_in_non_connected() + * @cm_ctx: connection manager context + * @cm_id: cm id + * + * If connect req is a reassoc req and received in not connected state + * + * Return: bool + */ +static bool cm_is_connect_id_reassoc_in_non_connected(struct cnx_mgr *cm_ctx, + wlan_cm_id cm_id) +{ + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + struct cm_req *cm_req; + uint32_t prefix = CM_ID_GET_PREFIX(cm_id); + bool is_reassoc = false; + + if (prefix != CONNECT_REQ_PREFIX) + return is_reassoc; + + cm_req_lock_acquire(cm_ctx); + qdf_list_peek_front(&cm_ctx->req_list, &cur_node); + while (cur_node) { + qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node); + cm_req = qdf_container_of(cur_node, struct cm_req, node); + + if (cm_req->cm_id == cm_id) { + if (cm_req->connect_req.req.reassoc_in_non_connected) + is_reassoc = true; + cm_req_lock_release(cm_ctx); + return is_reassoc; + } + + cur_node = next_node; + next_node = NULL; + } + cm_req_lock_release(cm_ctx); + + return is_reassoc; +} + QDF_STATUS cm_notify_connect_complete(struct cnx_mgr *cm_ctx, struct wlan_cm_connect_resp *resp) { + enum wlan_cm_sm_state sm_state; + + sm_state = cm_get_state(cm_ctx); + mlme_cm_connect_complete_ind(cm_ctx->vdev, resp); mlo_sta_link_connect_notify(cm_ctx->vdev, resp); + /* + * If connect req was a reassoc req and was received in not connected + * state send disconnect instead of connect resp to kernel to cleanup + * kernel flags + */ + if (QDF_IS_STATUS_ERROR(resp->connect_status) && + sm_state == WLAN_CM_S_INIT && + cm_is_connect_id_reassoc_in_non_connected(cm_ctx, resp->cm_id)) { + resp->send_disconnect = true; + mlme_debug(CM_PREFIX_FMT "Set send disconnect to true to indicate disconnect instaed of connect resp", + CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), + resp->cm_id)); + } mlme_cm_osif_connect_complete(cm_ctx->vdev, resp); cm_if_mgr_inform_connect_complete(cm_ctx->vdev, resp->connect_status); diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h b/umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h index b553ffcb5a..ba086e6f21 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h @@ -1193,4 +1193,12 @@ void cm_set_candidate_custom_sort_cb( qdf_list_t *list)); #endif + +/** + * cm_is_connect_req_reassoc() - Is connect req for reassoc + * @req: connect req + * + * Return: void + */ +bool cm_is_connect_req_reassoc(struct wlan_cm_connect_req *req); #endif /* __WLAN_CM_MAIN_API_H__ */ diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_roam_util.c b/umac/mlme/connection_mgr/core/src/wlan_cm_roam_util.c index cc8f8e303e..f74251eeaf 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_roam_util.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_roam_util.c @@ -72,9 +72,7 @@ QDF_STATUS cm_check_and_prepare_roam_req(struct cnx_mgr *cm_ctx, * Reject re-assoc unless freq along with prev bssid and one * of bssid or bssid hint is present. */ - if (!freq || qdf_is_macaddr_zero(&req->prev_bssid) || - (qdf_is_macaddr_zero(&req->bssid) && - qdf_is_macaddr_zero(&req->bssid_hint))) + if (!cm_is_connect_req_reassoc(req)) return QDF_STATUS_E_FAILURE; wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &bssid); diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_util.c b/umac/mlme/connection_mgr/core/src/wlan_cm_util.c index b389293c21..493be840e6 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_util.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_util.c @@ -1256,6 +1256,17 @@ void cm_fill_ml_partner_info(struct wlan_cm_connect_req *req, } #endif +bool cm_is_connect_req_reassoc(struct wlan_cm_connect_req *req) +{ + if (!qdf_is_macaddr_zero(&req->prev_bssid) && + (!qdf_is_macaddr_zero(&req->bssid) || + !qdf_is_macaddr_zero(&req->bssid_hint)) && + (req->chan_freq || req->chan_freq_hint)) + return true; + + return false; +} + bool cm_get_active_connect_req(struct wlan_objmgr_vdev *vdev, struct wlan_cm_vdev_connect_req *req) { diff --git a/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_public_struct.h b/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_public_struct.h index 6485fab140..38f6122722 100644 --- a/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_public_struct.h +++ b/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_public_struct.h @@ -188,6 +188,7 @@ enum wlan_cm_source { * for production. * @is_wps_connection: if its wps connection * @is_osen_connection: if its osen connection + * @reassoc_in_non_connected: if reassoc received in non connected * @dot11mode_filter: dot11mode filter used to restrict connection to * 11n/11ac/11ax. * @sae_pwe: SAE mechanism for PWE derivation @@ -216,7 +217,8 @@ struct wlan_cm_connect_req { struct element_info scan_ie; uint8_t force_rsne_override:1, is_wps_connection:1, - is_osen_connection:1; + is_osen_connection:1, + reassoc_in_non_connected:1; enum dot11_mode_filter dot11mode_filter; uint8_t sae_pwe; uint16_t ht_caps; @@ -483,6 +485,8 @@ struct wlan_roam_sync_info { * @is_reassoc: if response is for reassoc/roam * @is_ft: is FT reassoc * @is_assoc: if response is for assoc + * @send_disconnect: if disconnect needed to sent to kernel, for reassoc + * received in non connected state, this is to cleanup kernel * @cm_id: Connect manager id * @bssid: BSSID of the ap * @ssid: SSID of the connection @@ -502,7 +506,8 @@ struct wlan_cm_connect_resp { is_osen_connection:1, is_reassoc:1, is_ft:1, - is_assoc:1; + is_assoc:1, + send_disconnect:1; wlan_cm_id cm_id; struct qdf_mac_addr bssid; struct wlan_ssid ssid;