diff --git a/os_if/linux/mlme/src/wlan_cfg80211_cm_req.c b/os_if/linux/mlme/src/wlan_cfg80211_cm_req.c index a41277963eda..f102675860b3 100644 --- a/os_if/linux/mlme/src/wlan_cfg80211_cm_req.c +++ b/os_if/linux/mlme/src/wlan_cfg80211_cm_req.c @@ -324,7 +324,7 @@ int wlan_osif_cfg80211_connect(struct net_device *dev, return -ENOMEM; connect_req->vdev_id = vdev_id; - connect_req->source = CM_OSIF_CONENCT_REQ; + connect_req->source = CM_OSIF_CONNECT_REQ; if (req->bssid) qdf_mem_copy(connect_req->bssid.bytes, req->bssid, QDF_MAC_ADDR_SIZE); 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 22e4293f621f..ef84d9c1cb1a 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c @@ -398,15 +398,45 @@ QDF_STATUS cm_connect_complete(struct cnx_mgr *cm_ctx, QDF_STATUS cm_connect_start_req(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_req *req) { - struct cnx_mgr *cm_ctx = NULL; - struct cm_connect_req *cm_req = NULL; + struct cnx_mgr *cm_ctx; + struct cm_req *cm_req; + struct cm_connect_req *connect_req; + QDF_STATUS status; + + cm_ctx = cm_get_cm_ctx(vdev); + + if (!cm_ctx) { + mlme_err("cm ctx NULL"); + return QDF_STATUS_E_INVAL; + } + + /* This would be freed as part of list removal from req list */ + cm_req = qdf_mem_malloc(sizeof(*cm_req)); + if (!cm_req) + return QDF_STATUS_E_NOMEM; /* * Get WAPI/WPA/RSN IE and refill crypto params of req. - * Prepare cm_connect_req cm_req, get cm id and inform it to OSIF. - * store connect req to the cm ctx req_list */ - return cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_CONNECT_REQ, - sizeof(*cm_req), cm_req); + connect_req = &cm_req->connect_req; + connect_req->cm_id = cm_get_cm_id(cm_ctx, req->source); + cm_req->cm_id = connect_req->cm_id; + connect_req->req = *req; + + status = cm_add_req_to_list(cm_ctx, cm_req); + + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(cm_req); + return status; + } + + mlme_cm_osif_update_id_and_src(vdev, req->source, cm_req->cm_id); + + status = cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_CONNECT_REQ, + sizeof(*connect_req), connect_req); + if (QDF_IS_STATUS_ERROR(status)) + cm_delete_req_from_list(cm_ctx, cm_req->cm_id); + + return status; } diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_disconnect.c b/umac/mlme/connection_mgr/core/src/wlan_cm_disconnect.c index d763d1040900..fa5c88809633 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_disconnect.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_disconnect.c @@ -148,14 +148,43 @@ QDF_STATUS cm_disconnect_complete(struct cnx_mgr *cm_ctx, QDF_STATUS cm_disconnect_start_req(struct wlan_objmgr_vdev *vdev, struct wlan_cm_disconnect_req *req) { - struct cnx_mgr *cm_ctx = NULL; - struct cm_disconnect_req *cm_req = NULL; + struct cnx_mgr *cm_ctx; + struct cm_req *cm_req; + struct cm_disconnect_req *disconnect_req; + QDF_STATUS status; - /* - * Prepare cm_disconnect_req cm_req, get cm id and inform it to - * OSIF. store disconnect req to the cm ctx req_list - */ + cm_ctx = cm_get_cm_ctx(vdev); - return cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_REQ, - sizeof(*cm_req), cm_req); + if (!cm_ctx) { + mlme_err("cm ctx NULL"); + return QDF_STATUS_E_INVAL; + } + + /* This would be freed as part of list removal from req list */ + cm_req = qdf_mem_malloc(sizeof(*cm_req)); + + if (!cm_req) + return QDF_STATUS_E_NOMEM; + + disconnect_req = &cm_req->discon_req; + + disconnect_req->cm_id = cm_get_cm_id(cm_ctx, req->source); + cm_req->cm_id = disconnect_req->cm_id; + disconnect_req->req = *req; + + status = cm_add_req_to_list(cm_ctx, cm_req); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(cm_req); + return status; + } + + mlme_cm_osif_update_id_and_src(vdev, req->source, cm_req->cm_id); + + status = cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_REQ, + sizeof(*disconnect_req), disconnect_req); + + if (QDF_IS_STATUS_ERROR(status)) + cm_delete_req_from_list(cm_ctx, cm_req->cm_id); + + return status; } diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_main.c b/umac/mlme/connection_mgr/core/src/wlan_cm_main.c index cf816cca2121..102e00b1446c 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_main.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_main.c @@ -21,6 +21,48 @@ #include "wlan_cm_main.h" #include "wlan_cm_sm.h" +#ifdef WLAN_CM_USE_SPINLOCK +/** + * cm_req_lock_create - Create CM req SM mutex/spinlock + * @cm_ctx: connection manager ctx + * + * Creates CM SM mutex/spinlock + * + * Return: void + */ +static inline void +cm_req_lock_create(struct cnx_mgr *cm_ctx) +{ + qdf_spinlock_create(&cm_ctx->cm_req_lock); +} + +/** + * cm_req_lock_destroy - Destroy CM SM mutex/spinlock + * @cm_ctx: connection manager ctx + * + * Destroy CM SM mutex/spinlock + * + * Return: void + */ +static inline void +cm_req_lock_destroy(struct cnx_mgr *cm_ctx) +{ + qdf_spinlock_destroy(&cm_ctx->cm_req_lock); +} +#else +static inline void +cm_req_lock_create(struct cnx_mgr *cm_ctx) +{ + qdf_mutex_create(&cm_ctx->cm_req_lock); +} + +static inline void +cm_req_lock_destroy(struct cnx_mgr *cm_ctx) +{ + qdf_mutex_destroy(&cm_ctx->cm_req_lock); +} +#endif /* WLAN_CM_USE_SPINLOCK */ + QDF_STATUS wlan_cm_init(struct vdev_mlme_obj *vdev_mlme) { struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev; @@ -43,6 +85,7 @@ QDF_STATUS wlan_cm_init(struct vdev_mlme_obj *vdev_mlme) return QDF_STATUS_E_NOMEM; } qdf_list_create(&vdev_mlme->cnx_mgr_ctx->req_list, CM_MAX_REQ); + cm_req_lock_create(vdev_mlme->cnx_mgr_ctx); return QDF_STATUS_SUCCESS; } @@ -55,6 +98,7 @@ QDF_STATUS wlan_cm_deinit(struct vdev_mlme_obj *vdev_mlme) if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE) return QDF_STATUS_SUCCESS; + cm_req_lock_destroy(vdev_mlme->cnx_mgr_ctx); qdf_list_destroy(&vdev_mlme->cnx_mgr_ctx->req_list); cm_sm_destroy(vdev_mlme->cnx_mgr_ctx); qdf_mem_free(vdev_mlme->cnx_mgr_ctx); diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_main.h b/umac/mlme/connection_mgr/core/src/wlan_cm_main.h index f28662492fd1..5b04228d2429 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_main.h +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_main.h @@ -124,14 +124,12 @@ struct cm_disconnect_req { /** * struct cm_req - connect manager req * @node: connection manager req node - * @source: req source * @cm_id: cm id * @connect_req: connect req * @disconnect_req: disconnect req */ struct cm_req { qdf_list_node_t node; - enum wlan_cm_source source; wlan_cm_id cm_id; union { struct cm_connect_req connect_req; @@ -160,21 +158,29 @@ struct connect_ies { * @vdev: vdev back pointer * @sm: state machine * @req_list: connect/disconnect req list + * @cm_req_lock: lock to manupulate/read the cm req list * @disconnect_count: disconnect count * @connect_count: connect count * @force_rsne_override: if QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE is set by * framework * @req_ie: request ies for connect/disconnect set by osif/user separately from * connect req + * @global_cmd_id: global cmd id for getting cm id for connect/disconnect req */ struct cnx_mgr { struct wlan_objmgr_vdev *vdev; struct cm_state_sm sm; qdf_list_t req_list; +#ifdef WLAN_CM_USE_SPINLOCK + qdf_spinlock_t cm_req_lock; +#else + qdf_mutex_t cm_req_lock; +#endif uint8_t disconnect_count; uint8_t connect_count; bool force_rsne_override; struct connect_ies req_ie; + qdf_atomic_t global_cmd_id; }; /** 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 bf882ba5a307..b13726d1f098 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 @@ -24,8 +24,10 @@ #define __WLAN_CM_MAIN_API_H__ #include "wlan_cm_main.h" +#include /*************** CONNECT APIs ****************/ + /** * cm_connect_start() - This API will be called to initiate the connect * process @@ -112,6 +114,21 @@ QDF_STATUS cm_connect_complete(struct cnx_mgr *cm_ctx, QDF_STATUS cm_connect_start_req(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_req *req); +#ifdef WLAN_POLICY_MGR_ENABLE +/** + * cm_hw_mode_change_resp() - HW mode change response + * @pdev: pdev pointer + * @vdev_id: vdev id + * @cm_id: connection ID which gave the hw mode change request + * @status: status of the HW mode change. + * + * Return: QDF_STATUS + */ +void cm_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev, uint8_t vdev_id, + wlan_cm_id cm_id, QDF_STATUS status); + +#endif + /*************** DISCONNECT APIs ****************/ /** @@ -157,18 +174,72 @@ QDF_STATUS cm_disconnect_complete(struct cnx_mgr *cm_ctx, QDF_STATUS cm_disconnect_start_req(struct wlan_objmgr_vdev *vdev, struct wlan_cm_disconnect_req *req); +/*************** UTIL APIs ****************/ + /** - * cm_hw_mode_change_resp() - HW mode change response - * @pdev: pdev pointer - * @vdev_id: vdev id - * @cm_id: connection ID which gave the hw mode change request - * @status: status of the HW mode change. + * cm_get_cm_id() - Get unique cm id for connect/disconnect request + * @cm_ctx: connection manager context + * @source: source of the request (can be connect or disconnect request) + * + * Return: cm id + */ +wlan_cm_id cm_get_cm_id(struct cnx_mgr *cm_ctx, enum wlan_cm_source source); + +/** + * cm_get_cm_ctx() - Get connection manager context from vdev + * @vdev: vdev object pointer + * + * Return: pointer to connection manager context + */ +struct cnx_mgr *cm_get_cm_ctx(struct wlan_objmgr_vdev *vdev); + +/** + * cm_check_cmid_match_list_head() - check if list head command matches the + * given cm_id + * @cm_ctx: connection manager context + * @cm_id: cm id of connect/disconnect req + * + * Check if front req command matches the given + * cm_id, this can be used to check if the latest (head) is same we are + * trying to processing + * + * Return: true if match else false + */ +bool cm_check_cmid_match_list_head(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id); + +/** + * cm_delete_req_from_list() - Delete the request matching cm id + * @cm_ctx: connection manager context + * @cm_id: cm id of connect/disconnect req + * + * Context: Can be called from any context. * * Return: QDF_STATUS */ -#ifdef WLAN_POLICY_MGR_ENABLE -void cm_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev, uint8_t vdev_id, - wlan_cm_id cm_id, QDF_STATUS status); +QDF_STATUS cm_delete_req_from_list(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id); + +/** + * cm_add_req_to_list() - Add the request to request list in cm ctx + * @cm_ctx: connection manager context + * @cm_req: cm request + * + * Context: Can be called from any context. + * + * Return: QDF_STATUS + */ +QDF_STATUS cm_add_req_to_list(struct cnx_mgr *cm_ctx, struct cm_req *cm_req); + +/** + * cm_get_req_by_cm_id() - Get cm req matching the cm id + * @cm_ctx: connection manager context + * @cm_id: cm id of connect/disconnect req + * + * Context: Can be called from any context and to be used only after posting a + * msg to SM (ie holding the SM lock) to avoid use after free. also returned req + * should only be used till SM lock is hold. + * + * Return: cm req from the req list whose cm id matches the argument + */ +struct cm_req *cm_get_req_by_cm_id(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id); -#endif #endif /* __WLAN_CM_MAIN_API_H__ */ 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 66fce7700772..f1670ae2e453 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_util.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_util.c @@ -17,3 +17,194 @@ /** * DOC: Implements general util apis of connection manager */ + +#include "wlan_cm_main_api.h" +#include "wlan_scan_api.h" +#include "wlan_cm_public_struct.h" + +#define CONNECT_REQ_PREFIX 0x00C00000 +#define DISCONNECT_REQ_PREFIX 0x00D00000 +#define CM_ID_MASK 0x0000FFFF + +#define CM_ID_GET_PREFIX(cm_id) cm_id & 0xFFFF0000 + +static uint32_t cm_get_prefix_for_cm_id(enum wlan_cm_source source) { + switch (source) { + case CM_OSIF_CONNECT_REQ: + case CM_ROAMING: + return CONNECT_REQ_PREFIX; + default: + return DISCONNECT_REQ_PREFIX; + } +} + +wlan_cm_id cm_get_cm_id(struct cnx_mgr *cm_ctx, enum wlan_cm_source source) +{ + wlan_cm_id cmd_id; + uint32_t prefix; + + prefix = cm_get_prefix_for_cm_id(source); + + cmd_id = qdf_atomic_inc_return(&cm_ctx->global_cmd_id); + cmd_id = (cmd_id & CM_ID_MASK); + cmd_id = (cmd_id | prefix); + + return cmd_id; +} + +struct cnx_mgr *cm_get_cm_ctx(struct wlan_objmgr_vdev *vdev) +{ + struct vdev_mlme_obj *vdev_mlme; + + vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); + if (!vdev_mlme) + return NULL; + + return vdev_mlme->cnx_mgr_ctx; +} + + +#ifdef WLAN_CM_USE_SPINLOCK +/** + * cm_req_lock_acquire - acquire CM SM mutex/spinlock + * @cm_ctx: connection manager ctx + * + * acquire CM SM mutex/spinlock + * + * return: void + */ +static inline void cm_req_lock_acquire(struct cnx_mgr *cm_ctx) +{ + qdf_spinlock_acquire(&cm_ctx->cm_req_lock); +} + +/** + * cm_req_lock_release - release CM SM mutex/spinlock + * @cm_ctx: connection manager ctx + * + * release CM SM mutex/spinlock + * + * return: void + */ +static inline void cm_req_lock_release(struct cnx_mgr *cm_ctx) +{ + qdf_spinlock_release(&cm_ctx->cm_req_lock); +} +#else +static inline void cm_req_lock_acquire(struct cnx_mgr *cm_ctx) +{ + qdf_mutex_acquire(&cm_ctx->cm_req_lock); +} + +static inline void cm_req_lock_release(struct cnx_mgr *cm_ctx) +{ + qdf_mutex_release(&cm_ctx->cm_req_lock); +} +#endif /* WLAN_CM_USE_SPINLOCK */ + +bool cm_check_cmid_match_list_head(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id) +{ + qdf_list_node_t *cur_node = NULL; + struct cm_req *cm_req; + bool match = false; + + cm_req_lock_acquire(cm_ctx); + qdf_list_peek_front(&cm_ctx->req_list, &cur_node); + if (!cur_node) + goto exit; + + cm_req = qdf_container_of(cur_node, struct cm_req, node); + if (cm_req->cm_id == cm_id) + match = true; + +exit: + cm_req_lock_release(cm_ctx); + + return match; +} + +struct cm_req *cm_get_req_by_cm_id(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; + + 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) { + cm_req_lock_release(cm_ctx); + return cm_req; + } + + cur_node = next_node; + next_node = NULL; + } + cm_req_lock_release(cm_ctx); + + return NULL; +} + +QDF_STATUS cm_add_req_to_list(struct cnx_mgr *cm_ctx, struct cm_req *cm_req) +{ + uint32_t prefix = CM_ID_GET_PREFIX(cm_req->cm_id); + + cm_req_lock_acquire(cm_ctx); + if (qdf_list_size(&cm_ctx->req_list) >= CM_MAX_REQ) { + cm_req_lock_release(cm_ctx); + mlme_err("List full size %d", qdf_list_size(&cm_ctx->req_list)); + return QDF_STATUS_E_FAILURE; + } + + qdf_list_insert_front(&cm_ctx->req_list, &cm_req->node); + if (prefix == CONNECT_REQ_PREFIX) + cm_ctx->connect_count++; + else + cm_ctx->disconnect_count++; + cm_req_lock_release(cm_ctx); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cm_delete_req_from_list(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id) +{ + uint32_t prefix = CM_ID_GET_PREFIX(cm_id); + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + struct cm_req *cm_req = NULL; + + 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) + break; + + cur_node = next_node; + next_node = NULL; + cm_req = NULL; + } + + if (!cm_req) { + cm_req_lock_release(cm_ctx); + mlme_err("cm req id %d not found", cm_id); + return QDF_STATUS_E_FAILURE; + } + + qdf_list_remove_node(&cm_ctx->req_list, &cm_req->node); + if (prefix == CONNECT_REQ_PREFIX) { + cm_ctx->connect_count--; + wlan_scan_purge_results(cm_req->connect_req.candidate_list); + } else { + cm_ctx->disconnect_count--; + } + + qdf_mem_free(cm_req); + cm_req_lock_release(cm_ctx); + + return QDF_STATUS_SUCCESS; +} 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 784e3534702d..adff35c6de0b 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 @@ -100,7 +100,7 @@ struct wlan_fils_con_info { /** * enum wlan_cm_source - connection manager req source - * @CM_OSIF_CONENCT_REQ: Connect req initiated by OSIF or north bound + * @CM_OSIF_CONNECT_REQ: Connect req initiated by OSIF or north bound * @CM_ROAMING: Roaming request * @CM_OSIF_DISCONNECT: Disconnect req initiated by OSIF or north bound * @CM_PEER_DISCONNECT: Disconnect req initiated by peer sending deauth/disassoc @@ -111,7 +111,7 @@ struct wlan_fils_con_info { * @CM_ROAM_DISCONNECT: Disconnect req due to HO failure */ enum wlan_cm_source { - CM_OSIF_CONENCT_REQ, + CM_OSIF_CONNECT_REQ, CM_ROAMING, CM_OSIF_DISCONNECT, CM_PEER_DISCONNECT,