| /* |
| * Copyright (c) 2016-2017 The Linux Foundation. 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 copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| /** |
| * DOC: Public APIs to perform operations on Global objects |
| */ |
| #include <wlan_objmgr_cmn.h> |
| #include <wlan_objmgr_global_obj.h> |
| #include <wlan_objmgr_psoc_obj.h> |
| #include <wlan_objmgr_pdev_obj.h> |
| #include <wlan_objmgr_vdev_obj.h> |
| #include <wlan_objmgr_peer_obj.h> |
| #include <qdf_mem.h> |
| #include "wlan_objmgr_global_obj_i.h" |
| #include "wlan_objmgr_psoc_obj_i.h" |
| #include "wlan_objmgr_pdev_obj_i.h" |
| |
| |
| /** |
| ** APIs to Create/Delete Global object APIs |
| */ |
| static QDF_STATUS wlan_objmgr_pdev_object_status( |
| struct wlan_objmgr_pdev *pdev) |
| { |
| uint8_t id; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| wlan_pdev_obj_lock(pdev); |
| /* Iterate through all components to derive the object status */ |
| for (id = 0; id < WLAN_UMAC_MAX_COMPONENTS; id++) { |
| /* If component disabled, Ignore */ |
| if (pdev->obj_status[id] == QDF_STATUS_COMP_DISABLED) { |
| continue; |
| /* If component operates in Async, status is Partially created, |
| break */ |
| } else if (pdev->obj_status[id] == QDF_STATUS_COMP_ASYNC) { |
| if (pdev->pdev_comp_priv_obj[id] == NULL) { |
| status = QDF_STATUS_COMP_ASYNC; |
| break; |
| } |
| /* If component failed to allocate its object, treat it as |
| failure, complete object need to be cleaned up */ |
| } else if ((pdev->obj_status[id] == QDF_STATUS_E_NOMEM) || |
| (pdev->obj_status[id] == QDF_STATUS_E_FAILURE)) { |
| status = QDF_STATUS_E_FAILURE; |
| break; |
| } |
| } |
| wlan_pdev_obj_unlock(pdev); |
| return status; |
| } |
| |
| static QDF_STATUS wlan_objmgr_pdev_obj_free(struct wlan_objmgr_pdev *pdev) |
| { |
| |
| uint8_t pdev_id; |
| |
| if (pdev == NULL) { |
| obj_mgr_err("pdev obj is NULL"); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); |
| |
| /* Detach PDEV from PSOC PDEV's list */ |
| if (wlan_objmgr_psoc_pdev_detach(pdev->pdev_objmgr.wlan_psoc, pdev) == |
| QDF_STATUS_E_FAILURE) { |
| obj_mgr_err("PSOC PDEV detach failed: pdev-id: %d", pdev_id); |
| return QDF_STATUS_E_FAILURE; |
| } |
| qdf_spinlock_destroy(&pdev->pdev_lock); |
| qdf_mem_free(pdev); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| struct wlan_objmgr_pdev *wlan_objmgr_pdev_obj_create( |
| struct wlan_objmgr_psoc *psoc, |
| struct pdev_osif_priv *osdev_priv) |
| { |
| struct wlan_objmgr_pdev *pdev; |
| uint8_t id; |
| wlan_objmgr_pdev_create_handler handler; |
| wlan_objmgr_pdev_status_handler s_handler; |
| void *arg; |
| QDF_STATUS obj_status; |
| |
| if (psoc == NULL) { |
| obj_mgr_err("psoc is NULL"); |
| return NULL; |
| } |
| /* Allocate PDEV object's memory */ |
| pdev = qdf_mem_malloc(sizeof(*pdev)); |
| if (pdev == NULL) { |
| obj_mgr_err("pdev alloc failed"); |
| return NULL; |
| } |
| /* Attach PDEV with PSOC */ |
| if (wlan_objmgr_psoc_pdev_attach(psoc, pdev) |
| != QDF_STATUS_SUCCESS) { |
| obj_mgr_err("pdev psoc attach failed"); |
| qdf_mem_free(pdev); |
| return NULL; |
| } |
| /* Save PSOC object pointer in PDEV */ |
| wlan_pdev_set_psoc(pdev, psoc); |
| /* Initialize PDEV spinlock */ |
| qdf_spinlock_create(&pdev->pdev_lock); |
| /* Initialize PDEV's VDEV list, assign default values */ |
| qdf_list_create(&pdev->pdev_objmgr.wlan_vdev_list, |
| WLAN_UMAC_PDEV_MAX_VDEVS); |
| pdev->pdev_objmgr.wlan_vdev_count = 0; |
| pdev->pdev_objmgr.max_vdev_count = WLAN_UMAC_PDEV_MAX_VDEVS; |
| pdev->pdev_objmgr.wlan_peer_count = 0; |
| pdev->pdev_objmgr.max_peer_count = WLAN_UMAC_PSOC_MAX_PEERS; |
| /* Save HDD/OSIF pointer */ |
| pdev->pdev_nif.pdev_ospriv = osdev_priv; |
| qdf_atomic_init(&pdev->pdev_objmgr.ref_cnt); |
| pdev->pdev_objmgr.print_cnt = 0; |
| wlan_objmgr_pdev_get_ref(pdev, WLAN_OBJMGR_ID); |
| /* Invoke registered create handlers */ |
| for (id = 0; id < WLAN_UMAC_MAX_COMPONENTS; id++) { |
| handler = g_umac_glb_obj->pdev_create_handler[id]; |
| arg = g_umac_glb_obj->pdev_create_handler_arg[id]; |
| if (handler != NULL) |
| pdev->obj_status[id] = handler(pdev, arg); |
| else |
| pdev->obj_status[id] = QDF_STATUS_COMP_DISABLED; |
| } |
| /* Derive object status */ |
| obj_status = wlan_objmgr_pdev_object_status(pdev); |
| |
| if (obj_status == QDF_STATUS_SUCCESS) { |
| /* Object status is SUCCESS, Object is created */ |
| pdev->obj_state = WLAN_OBJ_STATE_CREATED; |
| /* Invoke component registered status handlers */ |
| for (id = 0; id < WLAN_UMAC_MAX_COMPONENTS; id++) { |
| s_handler = g_umac_glb_obj->pdev_status_handler[id]; |
| arg = g_umac_glb_obj->pdev_status_handler_arg[id]; |
| if (s_handler != NULL) { |
| s_handler(pdev, arg, |
| QDF_STATUS_SUCCESS); |
| } |
| } |
| /* Few components operates in Asynchrous communction, Object state |
| partially created */ |
| } else if (obj_status == QDF_STATUS_COMP_ASYNC) { |
| pdev->obj_state = WLAN_OBJ_STATE_PARTIALLY_CREATED; |
| /* Component object failed to be created, clean up the object */ |
| } else if (obj_status == QDF_STATUS_E_FAILURE) { |
| /* Clean up the psoc */ |
| obj_mgr_err("PDEV component objects allocation failed"); |
| wlan_objmgr_pdev_obj_delete(pdev); |
| return NULL; |
| } |
| return pdev; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_pdev_obj_create); |
| |
| static QDF_STATUS wlan_objmgr_pdev_obj_destroy(struct wlan_objmgr_pdev *pdev) |
| { |
| uint8_t id; |
| wlan_objmgr_pdev_destroy_handler handler; |
| QDF_STATUS obj_status; |
| void *arg; |
| uint8_t pdev_id; |
| |
| if (pdev == NULL) { |
| obj_mgr_err("pdev is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); |
| |
| if (pdev->obj_state != WLAN_OBJ_STATE_LOGICALLY_DELETED) { |
| obj_mgr_err("pdev object delete is not invoked: pdev-id:%d", |
| pdev_id); |
| WLAN_OBJMGR_BUG(0); |
| } |
| |
| /* Invoke registered destroy handlers */ |
| for (id = 0; id < WLAN_UMAC_MAX_COMPONENTS; id++) { |
| handler = g_umac_glb_obj->pdev_destroy_handler[id]; |
| arg = g_umac_glb_obj->pdev_destroy_handler_arg[id]; |
| if (handler != NULL) |
| pdev->obj_status[id] = handler(pdev, arg); |
| else |
| pdev->obj_status[id] = QDF_STATUS_COMP_DISABLED; |
| } |
| /* Derive object status */ |
| obj_status = wlan_objmgr_pdev_object_status(pdev); |
| |
| if (obj_status == QDF_STATUS_E_FAILURE) { |
| obj_mgr_err("PDEV component objects destroy failed: pdev-id:%d", |
| pdev_id); |
| /* Ideally should not happen */ |
| /* This leads to memleak ??? how to handle */ |
| QDF_BUG(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* Deletion is in progress */ |
| if (obj_status == QDF_STATUS_COMP_ASYNC) { |
| pdev->obj_state = WLAN_OBJ_STATE_PARTIALLY_DELETED; |
| return QDF_STATUS_COMP_ASYNC; |
| } |
| |
| /* Free PDEV object */ |
| return wlan_objmgr_pdev_obj_free(pdev); |
| } |
| |
| QDF_STATUS wlan_objmgr_pdev_obj_delete(struct wlan_objmgr_pdev *pdev) |
| { |
| uint8_t print_idx; |
| |
| if (pdev == NULL) { |
| obj_mgr_err("pdev is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| print_idx = qdf_get_pidx(); |
| if (qdf_print_is_verbose_enabled(print_idx, QDF_MODULE_ID_OBJ_MGR, |
| QDF_TRACE_LEVEL_DEBUG)) { |
| obj_mgr_debug("Logically deleting the pdev(id:%d)", |
| pdev->pdev_objmgr.wlan_pdev_id); |
| wlan_objmgr_print_ref_ids(pdev->pdev_objmgr.ref_id_dbg); |
| } |
| |
| /* |
| * Update PDEV object state to LOGICALLY DELETED |
| * It prevents further access of this object |
| */ |
| wlan_pdev_obj_lock(pdev); |
| pdev->obj_state = WLAN_OBJ_STATE_LOGICALLY_DELETED; |
| wlan_pdev_obj_unlock(pdev); |
| wlan_objmgr_pdev_release_ref(pdev, WLAN_OBJMGR_ID); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_pdev_obj_delete); |
| |
| /** |
| ** APIs to attach/detach component objects |
| */ |
| QDF_STATUS wlan_objmgr_pdev_component_obj_attach( |
| struct wlan_objmgr_pdev *pdev, |
| enum wlan_umac_comp_id id, |
| void *comp_priv_obj, |
| QDF_STATUS status) |
| { |
| uint8_t i; |
| wlan_objmgr_pdev_status_handler s_hlr; |
| void *a; |
| QDF_STATUS obj_status; |
| |
| /* component id is invalid */ |
| if (id >= WLAN_UMAC_MAX_COMPONENTS) { |
| obj_mgr_err("component-id %d is not supported", id); |
| return QDF_STATUS_MAXCOMP_FAIL; |
| } |
| wlan_pdev_obj_lock(pdev); |
| /* If there is a valid entry, return failure */ |
| if (pdev->pdev_comp_priv_obj[id] != NULL) { |
| obj_mgr_err("component-%d already have valid pointer", id); |
| wlan_pdev_obj_unlock(pdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* Save component's pointer and status */ |
| pdev->pdev_comp_priv_obj[id] = comp_priv_obj; |
| pdev->obj_status[id] = status; |
| |
| wlan_pdev_obj_unlock(pdev); |
| |
| if (pdev->obj_state != WLAN_OBJ_STATE_PARTIALLY_CREATED) |
| return QDF_STATUS_SUCCESS; |
| /** |
| * If PDEV object status is partially created means, this API is |
| * invoked with differnt context, this block should be executed for |
| * async components only |
| */ |
| /* Derive status */ |
| obj_status = wlan_objmgr_pdev_object_status(pdev); |
| /* STATUS_SUCCESS means, object is CREATED */ |
| if (obj_status == QDF_STATUS_SUCCESS) |
| pdev->obj_state = WLAN_OBJ_STATE_CREATED; |
| /* update state as CREATION failed, caller has to delete the |
| PDEV object */ |
| else if (obj_status == QDF_STATUS_E_FAILURE) |
| pdev->obj_state = WLAN_OBJ_STATE_CREATION_FAILED; |
| /* Notify components about the CREATION success/failure */ |
| if ((obj_status == QDF_STATUS_SUCCESS) || |
| (obj_status == QDF_STATUS_E_FAILURE)) { |
| /* nofity object status */ |
| for (i = 0; i < WLAN_UMAC_MAX_COMPONENTS; i++) { |
| s_hlr = g_umac_glb_obj->pdev_status_handler[i]; |
| a = g_umac_glb_obj->pdev_status_handler_arg[i]; |
| if (s_hlr != NULL) |
| s_hlr(pdev, a, obj_status); |
| } |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_pdev_component_obj_attach); |
| |
| QDF_STATUS wlan_objmgr_pdev_component_obj_detach( |
| struct wlan_objmgr_pdev *pdev, |
| enum wlan_umac_comp_id id, |
| void *comp_priv_obj) |
| { |
| QDF_STATUS obj_status; |
| |
| /* component id is invalid */ |
| if (id >= WLAN_UMAC_MAX_COMPONENTS) |
| return QDF_STATUS_MAXCOMP_FAIL; |
| |
| wlan_pdev_obj_lock(pdev); |
| /* If there is a invalid entry, return failure */ |
| if (pdev->pdev_comp_priv_obj[id] != comp_priv_obj) { |
| pdev->obj_status[id] = QDF_STATUS_E_FAILURE; |
| wlan_pdev_obj_unlock(pdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* Reset pointers to NULL, update the status*/ |
| pdev->pdev_comp_priv_obj[id] = NULL; |
| pdev->obj_status[id] = QDF_STATUS_SUCCESS; |
| wlan_pdev_obj_unlock(pdev); |
| |
| /* If PDEV object status is partially destroyed means, this API is |
| invoked with differnt context, this block should be executed for async |
| components only */ |
| if ((pdev->obj_state == WLAN_OBJ_STATE_PARTIALLY_DELETED) || |
| (pdev->obj_state == WLAN_OBJ_STATE_COMP_DEL_PROGRESS)) { |
| /* Derive object status */ |
| obj_status = wlan_objmgr_pdev_object_status(pdev); |
| if (obj_status == QDF_STATUS_SUCCESS) { |
| /*Update the status as Deleted, if full object |
| deletion is in progress */ |
| if (pdev->obj_state == |
| WLAN_OBJ_STATE_PARTIALLY_DELETED) |
| pdev->obj_state = WLAN_OBJ_STATE_DELETED; |
| /* Move to creation state, since this component |
| deletion alone requested */ |
| if (pdev->obj_state == |
| WLAN_OBJ_STATE_COMP_DEL_PROGRESS) |
| pdev->obj_state = WLAN_OBJ_STATE_CREATED; |
| /* Object status is failure */ |
| } else if (obj_status == QDF_STATUS_E_FAILURE) { |
| /*Update the status as Deletion failed, if full object |
| deletion is in progress */ |
| if (pdev->obj_state == |
| WLAN_OBJ_STATE_PARTIALLY_DELETED) |
| pdev->obj_state = |
| WLAN_OBJ_STATE_DELETION_FAILED; |
| /* Move to creation state, since this component |
| deletion alone requested (do not block other |
| components)*/ |
| if (pdev->obj_state == |
| WLAN_OBJ_STATE_COMP_DEL_PROGRESS) |
| pdev->obj_state = WLAN_OBJ_STATE_CREATED; |
| } |
| |
| /* Delete pdev object */ |
| if ((obj_status == QDF_STATUS_SUCCESS) && |
| (pdev->obj_state == WLAN_OBJ_STATE_DELETED)) { |
| /* Free PDEV object */ |
| return wlan_objmgr_pdev_obj_free(pdev); |
| } |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_pdev_component_obj_detach); |
| |
| /** |
| ** APIs to operations on pdev objects |
| */ |
| static void wlan_objmgr_pdev_vdev_iterate_peers(struct wlan_objmgr_pdev *pdev, |
| struct wlan_objmgr_vdev *vdev, |
| wlan_objmgr_pdev_op_handler handler, |
| void *arg, uint8_t lock_free_op, |
| wlan_objmgr_ref_dbgid dbg_id) |
| { |
| qdf_list_t *peer_list = NULL; |
| struct wlan_objmgr_peer *peer = NULL; |
| struct wlan_objmgr_peer *peer_next = NULL; |
| |
| /* Iterating through vdev's peer list, so lock is |
| needed */ |
| if (!lock_free_op) |
| wlan_vdev_obj_lock(vdev); |
| /* Get peer list of the vdev */ |
| peer_list = &vdev->vdev_objmgr.wlan_peer_list; |
| if (peer_list != NULL) { |
| peer = wlan_vdev_peer_list_peek_head(peer_list); |
| while (peer != NULL) { |
| /* Get next peer pointer */ |
| peer_next = wlan_peer_get_next_peer_of_vdev(peer_list, |
| peer); |
| /* Increment ref count, to hold the |
| peer pointer */ |
| if (wlan_objmgr_peer_try_get_ref(peer, dbg_id) == |
| QDF_STATUS_SUCCESS) { |
| /* Invoke the handler */ |
| handler(pdev, (void *)peer, arg); |
| wlan_objmgr_peer_release_ref(peer, dbg_id); |
| } |
| peer = peer_next; |
| } |
| } |
| if (!lock_free_op) |
| wlan_vdev_obj_unlock(vdev); |
| } |
| |
| QDF_STATUS wlan_objmgr_pdev_iterate_obj_list( |
| struct wlan_objmgr_pdev *pdev, |
| enum wlan_objmgr_obj_type obj_type, |
| wlan_objmgr_pdev_op_handler handler, |
| void *arg, uint8_t lock_free_op, |
| wlan_objmgr_ref_dbgid dbg_id) |
| { |
| struct wlan_objmgr_pdev_objmgr *objmgr = &pdev->pdev_objmgr; |
| qdf_list_t *vdev_list = NULL; |
| struct wlan_objmgr_vdev *vdev = NULL; |
| struct wlan_objmgr_vdev *vdev_next = NULL; |
| |
| /* If caller requests for lock free opeation, do not acquire |
| handler will handle the synchronization*/ |
| if (!lock_free_op) |
| wlan_pdev_obj_lock(pdev); |
| /* VDEV list */ |
| vdev_list = &objmgr->wlan_vdev_list; |
| switch (obj_type) { |
| case WLAN_VDEV_OP: |
| /* Iterate through all VDEV object, and invoke handler for each |
| VDEV object */ |
| vdev = wlan_pdev_vdev_list_peek_head(vdev_list); |
| while (vdev != NULL) { |
| /* |
| * Get next vdev (handler can be invoked for |
| * vdev deletion also |
| */ |
| vdev_next = wlan_vdev_get_next_vdev_of_pdev(vdev_list, |
| vdev); |
| if (wlan_objmgr_vdev_try_get_ref(vdev, dbg_id) == |
| QDF_STATUS_SUCCESS) { |
| handler(pdev, (void *)vdev, arg); |
| wlan_objmgr_vdev_release_ref(vdev, dbg_id); |
| } |
| vdev = vdev_next; |
| } |
| break; |
| case WLAN_PEER_OP: |
| vdev = wlan_pdev_vdev_list_peek_head(vdev_list); |
| while (vdev != NULL) { |
| /* Get Next VDEV */ |
| vdev_next = wlan_vdev_get_next_vdev_of_pdev(vdev_list, |
| vdev); |
| if (wlan_objmgr_vdev_try_get_ref(vdev, dbg_id) == |
| QDF_STATUS_SUCCESS) { |
| wlan_objmgr_pdev_vdev_iterate_peers(pdev, vdev, |
| handler, arg, |
| lock_free_op, |
| dbg_id); |
| wlan_objmgr_vdev_release_ref(vdev, dbg_id); |
| } |
| vdev = vdev_next; |
| } |
| break; |
| default: |
| break; |
| } |
| if (!lock_free_op) |
| wlan_pdev_obj_unlock(pdev); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_pdev_iterate_obj_list); |
| |
| QDF_STATUS wlan_objmgr_trigger_pdev_comp_priv_object_creation( |
| struct wlan_objmgr_pdev *pdev, |
| enum wlan_umac_comp_id id) |
| { |
| wlan_objmgr_pdev_create_handler handler; |
| void *arg; |
| QDF_STATUS obj_status = QDF_STATUS_SUCCESS; |
| |
| /* Component id is invalid */ |
| if (id >= WLAN_UMAC_MAX_COMPONENTS) |
| return QDF_STATUS_MAXCOMP_FAIL; |
| |
| wlan_pdev_obj_lock(pdev); |
| /* If component object is already created, delete old |
| component object, then invoke creation */ |
| if (pdev->pdev_comp_priv_obj[id] != NULL) { |
| wlan_pdev_obj_unlock(pdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| wlan_pdev_obj_unlock(pdev); |
| |
| /* Invoke registered create handlers */ |
| handler = g_umac_glb_obj->pdev_create_handler[id]; |
| arg = g_umac_glb_obj->pdev_create_handler_arg[id]; |
| if (handler != NULL) |
| pdev->obj_status[id] = handler(pdev, arg); |
| else |
| return QDF_STATUS_E_FAILURE; |
| /* If object status is created, then only handle this object status */ |
| if (pdev->obj_state == WLAN_OBJ_STATE_CREATED) { |
| /* Derive object status */ |
| obj_status = wlan_objmgr_pdev_object_status(pdev); |
| /* Move PDEV object state to Partially created state */ |
| if (obj_status == QDF_STATUS_COMP_ASYNC) { |
| /*TODO atomic */ |
| pdev->obj_state = WLAN_OBJ_STATE_PARTIALLY_CREATED; |
| } |
| } |
| return obj_status; |
| } |
| |
| QDF_STATUS wlan_objmgr_trigger_pdev_comp_priv_object_deletion( |
| struct wlan_objmgr_pdev *pdev, |
| enum wlan_umac_comp_id id) |
| { |
| wlan_objmgr_pdev_destroy_handler handler; |
| void *arg; |
| QDF_STATUS obj_status = QDF_STATUS_SUCCESS; |
| |
| /* component id is invalid */ |
| if (id >= WLAN_UMAC_MAX_COMPONENTS) |
| return QDF_STATUS_MAXCOMP_FAIL; |
| |
| wlan_pdev_obj_lock(pdev); |
| /* Component object was never created, invalid operation */ |
| if (pdev->pdev_comp_priv_obj[id] == NULL) { |
| wlan_pdev_obj_unlock(pdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| wlan_pdev_obj_unlock(pdev); |
| |
| /* Invoke registered create handlers */ |
| handler = g_umac_glb_obj->pdev_destroy_handler[id]; |
| arg = g_umac_glb_obj->pdev_destroy_handler_arg[id]; |
| if (handler != NULL) |
| pdev->obj_status[id] = handler(pdev, arg); |
| else |
| return QDF_STATUS_E_FAILURE; |
| |
| /* If object status is created, then only handle this object status */ |
| if (pdev->obj_state == WLAN_OBJ_STATE_CREATED) { |
| obj_status = wlan_objmgr_pdev_object_status(pdev); |
| /* move object state to DEL progress */ |
| if (obj_status == QDF_STATUS_COMP_ASYNC) |
| pdev->obj_state = WLAN_OBJ_STATE_COMP_DEL_PROGRESS; |
| } |
| return obj_status; |
| } |
| |
| static void wlan_obj_pdev_vdevlist_add_tail(qdf_list_t *obj_list, |
| struct wlan_objmgr_vdev *obj) |
| { |
| qdf_list_insert_back(obj_list, &obj->vdev_node); |
| } |
| |
| static QDF_STATUS wlan_obj_pdev_vdevlist_remove_vdev( |
| qdf_list_t *obj_list, |
| struct wlan_objmgr_vdev *vdev) |
| { |
| qdf_list_node_t *vdev_node = NULL; |
| |
| if (vdev == NULL) |
| return QDF_STATUS_E_FAILURE; |
| /* get vdev list node element */ |
| vdev_node = &vdev->vdev_node; |
| /* list is empty, return failure */ |
| if (qdf_list_remove_node(obj_list, vdev_node) != QDF_STATUS_SUCCESS) |
| return QDF_STATUS_E_FAILURE; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS wlan_objmgr_pdev_vdev_attach(struct wlan_objmgr_pdev *pdev, |
| struct wlan_objmgr_vdev *vdev) |
| { |
| struct wlan_objmgr_pdev_objmgr *objmgr = &pdev->pdev_objmgr; |
| |
| wlan_pdev_obj_lock(pdev); |
| /* If Max vdev count exceeds, return failure */ |
| if (objmgr->wlan_vdev_count > objmgr->max_vdev_count) { |
| wlan_pdev_obj_unlock(pdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* Add vdev to pdev's vdev list */ |
| wlan_obj_pdev_vdevlist_add_tail(&objmgr->wlan_vdev_list, vdev); |
| /* Increment pdev ref count to make sure it won't be destroyed before */ |
| wlan_objmgr_pdev_get_ref(pdev, WLAN_OBJMGR_ID); |
| /* Increment vdev count of pdev */ |
| objmgr->wlan_vdev_count++; |
| wlan_pdev_obj_unlock(pdev); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS wlan_objmgr_pdev_vdev_detach(struct wlan_objmgr_pdev *pdev, |
| struct wlan_objmgr_vdev *vdev) |
| { |
| struct wlan_objmgr_pdev_objmgr *objmgr = &pdev->pdev_objmgr; |
| |
| wlan_pdev_obj_lock(pdev); |
| /* if vdev count is 0, return failure */ |
| if (objmgr->wlan_vdev_count == 0) { |
| wlan_pdev_obj_unlock(pdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* remove vdev from pdev's vdev list */ |
| wlan_obj_pdev_vdevlist_remove_vdev(&objmgr->wlan_vdev_list, vdev); |
| /* decrement vdev count */ |
| objmgr->wlan_vdev_count--; |
| wlan_pdev_obj_unlock(pdev); |
| /* Decrement pdev ref count since vdev is releasing reference */ |
| wlan_objmgr_pdev_release_ref(pdev, WLAN_OBJMGR_ID); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| struct wlan_objmgr_vdev *wlan_objmgr_get_vdev_by_id_from_pdev( |
| struct wlan_objmgr_pdev *pdev, uint8_t vdev_id, |
| wlan_objmgr_ref_dbgid dbg_id) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_vdev *vdev_next; |
| struct wlan_objmgr_pdev_objmgr *objmgr; |
| qdf_list_t *vdev_list; |
| |
| wlan_pdev_obj_lock(pdev); |
| |
| objmgr = &pdev->pdev_objmgr; |
| vdev_list = &objmgr->wlan_vdev_list; |
| /* Get first vdev */ |
| vdev = wlan_pdev_vdev_list_peek_head(vdev_list); |
| /* Iterate through pdev's vdev list, till vdev id matches with |
| entry of vdev list */ |
| while (vdev != NULL) { |
| if (wlan_vdev_get_id(vdev) == vdev_id) { |
| if (wlan_objmgr_vdev_try_get_ref(vdev, dbg_id) != |
| QDF_STATUS_SUCCESS) |
| vdev = NULL; |
| |
| wlan_pdev_obj_unlock(pdev); |
| return vdev; |
| } |
| /* get next vdev */ |
| vdev_next = wlan_vdev_get_next_vdev_of_pdev(vdev_list, vdev); |
| vdev = vdev_next; |
| } |
| wlan_pdev_obj_unlock(pdev); |
| return NULL; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_get_vdev_by_id_from_pdev); |
| |
| struct wlan_objmgr_vdev *wlan_objmgr_get_vdev_by_id_from_pdev_no_state( |
| struct wlan_objmgr_pdev *pdev, uint8_t vdev_id, |
| wlan_objmgr_ref_dbgid dbg_id) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_vdev *vdev_next; |
| struct wlan_objmgr_pdev_objmgr *objmgr; |
| qdf_list_t *vdev_list; |
| |
| wlan_pdev_obj_lock(pdev); |
| |
| objmgr = &pdev->pdev_objmgr; |
| vdev_list = &objmgr->wlan_vdev_list; |
| /* Get first vdev */ |
| vdev = wlan_pdev_vdev_list_peek_head(vdev_list); |
| /** |
| * Iterate through pdev's vdev list, till vdev id matches with |
| * entry of vdev list |
| */ |
| while (vdev != NULL) { |
| if (wlan_vdev_get_id(vdev) == vdev_id) { |
| wlan_objmgr_vdev_get_ref(vdev, dbg_id); |
| wlan_pdev_obj_unlock(pdev); |
| |
| return vdev; |
| } |
| /* get next vdev */ |
| vdev_next = wlan_vdev_get_next_vdev_of_pdev(vdev_list, vdev); |
| vdev = vdev_next; |
| } |
| wlan_pdev_obj_unlock(pdev); |
| |
| return NULL; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_get_vdev_by_id_from_pdev_no_state); |
| |
| struct wlan_objmgr_vdev *wlan_objmgr_get_vdev_by_macaddr_from_pdev( |
| struct wlan_objmgr_pdev *pdev, uint8_t *macaddr, |
| wlan_objmgr_ref_dbgid dbg_id) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_vdev *vdev_next; |
| struct wlan_objmgr_pdev_objmgr *objmgr; |
| qdf_list_t *vdev_list; |
| |
| wlan_pdev_obj_lock(pdev); |
| objmgr = &pdev->pdev_objmgr; |
| vdev_list = &objmgr->wlan_vdev_list; |
| /* Get first vdev */ |
| vdev = wlan_pdev_vdev_list_peek_head(vdev_list); |
| /* Iterate through pdev's vdev list, till vdev macaddr matches with |
| entry of vdev list */ |
| while (vdev != NULL) { |
| if (WLAN_ADDR_EQ(wlan_vdev_mlme_get_macaddr(vdev), macaddr) |
| == QDF_STATUS_SUCCESS) { |
| if (wlan_objmgr_vdev_try_get_ref(vdev, dbg_id) != |
| QDF_STATUS_SUCCESS) |
| vdev = NULL; |
| |
| wlan_pdev_obj_unlock(pdev); |
| return vdev; |
| } |
| /* get next vdev */ |
| vdev_next = wlan_vdev_get_next_vdev_of_pdev(vdev_list, vdev); |
| vdev = vdev_next; |
| } |
| wlan_pdev_obj_unlock(pdev); |
| |
| return NULL; |
| } |
| |
| struct wlan_objmgr_vdev *wlan_objmgr_get_vdev_by_macaddr_from_pdev_no_state( |
| struct wlan_objmgr_pdev *pdev, uint8_t *macaddr, |
| wlan_objmgr_ref_dbgid dbg_id) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_vdev *vdev_next; |
| struct wlan_objmgr_pdev_objmgr *objmgr; |
| qdf_list_t *vdev_list; |
| |
| wlan_pdev_obj_lock(pdev); |
| objmgr = &pdev->pdev_objmgr; |
| vdev_list = &objmgr->wlan_vdev_list; |
| /* Get first vdev */ |
| vdev = wlan_pdev_vdev_list_peek_head(vdev_list); |
| /* Iterate through pdev's vdev list, till vdev macaddr matches with |
| entry of vdev list */ |
| while (vdev != NULL) { |
| if (WLAN_ADDR_EQ(wlan_vdev_mlme_get_macaddr(vdev), macaddr) |
| == QDF_STATUS_SUCCESS) { |
| wlan_objmgr_vdev_get_ref(vdev, dbg_id); |
| wlan_pdev_obj_unlock(pdev); |
| |
| return vdev; |
| } |
| /* get next vdev */ |
| vdev_next = wlan_vdev_get_next_vdev_of_pdev(vdev_list, vdev); |
| vdev = vdev_next; |
| } |
| wlan_pdev_obj_unlock(pdev); |
| |
| return NULL; |
| } |
| |
| void *wlan_objmgr_pdev_get_comp_private_obj( |
| struct wlan_objmgr_pdev *pdev, |
| enum wlan_umac_comp_id id) |
| { |
| void *comp_priv_obj; |
| |
| /* component id is invalid */ |
| if (id >= WLAN_UMAC_MAX_COMPONENTS) { |
| QDF_BUG(0); |
| return NULL; |
| } |
| |
| if (pdev == NULL) { |
| QDF_BUG(0); |
| return NULL; |
| } |
| |
| comp_priv_obj = pdev->pdev_comp_priv_obj[id]; |
| |
| return comp_priv_obj; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_pdev_get_comp_private_obj); |
| |
| void wlan_objmgr_pdev_get_ref(struct wlan_objmgr_pdev *pdev, |
| wlan_objmgr_ref_dbgid id) |
| { |
| if (pdev == NULL) { |
| obj_mgr_err("pdev obj is NULL"); |
| QDF_ASSERT(0); |
| return; |
| } |
| qdf_atomic_inc(&pdev->pdev_objmgr.ref_cnt); |
| qdf_atomic_inc(&pdev->pdev_objmgr.ref_id_dbg[id]); |
| |
| return; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_pdev_get_ref); |
| |
| QDF_STATUS wlan_objmgr_pdev_try_get_ref(struct wlan_objmgr_pdev *pdev, |
| wlan_objmgr_ref_dbgid id) |
| { |
| uint8_t pdev_id; |
| |
| if (pdev == NULL) { |
| obj_mgr_err("pdev obj is NULL"); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| wlan_pdev_obj_lock(pdev); |
| pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); |
| if (pdev->obj_state == WLAN_OBJ_STATE_LOGICALLY_DELETED) { |
| wlan_pdev_obj_unlock(pdev); |
| if (pdev->pdev_objmgr.print_cnt++ <= |
| WLAN_OBJMGR_RATELIMIT_THRESH) |
| obj_mgr_err("[Ref id: %d] pdev [%d] is in L-Del state", |
| id, pdev_id); |
| |
| return QDF_STATUS_E_RESOURCES; |
| } |
| |
| wlan_objmgr_pdev_get_ref(pdev, id); |
| wlan_pdev_obj_unlock(pdev); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_pdev_try_get_ref); |
| |
| void wlan_objmgr_pdev_release_ref(struct wlan_objmgr_pdev *pdev, |
| wlan_objmgr_ref_dbgid id) |
| { |
| uint8_t pdev_id; |
| |
| if (pdev == NULL) { |
| obj_mgr_err("pdev obj is NULL"); |
| QDF_ASSERT(0); |
| return; |
| } |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); |
| |
| if (!qdf_atomic_read(&pdev->pdev_objmgr.ref_id_dbg[id])) { |
| obj_mgr_err("pdev (id:%d)ref cnt was not taken by %d", |
| pdev_id, id); |
| wlan_objmgr_print_ref_ids(pdev->pdev_objmgr.ref_id_dbg); |
| WLAN_OBJMGR_BUG(0); |
| } |
| |
| if (!qdf_atomic_read(&pdev->pdev_objmgr.ref_cnt)) { |
| obj_mgr_err("pdev ref cnt is 0: pdev-id:%d", pdev_id); |
| WLAN_OBJMGR_BUG(0); |
| return; |
| } |
| |
| qdf_atomic_dec(&pdev->pdev_objmgr.ref_id_dbg[id]); |
| /* Decrement ref count, free pdev, if ref count == 0 */ |
| if (qdf_atomic_dec_and_test(&pdev->pdev_objmgr.ref_cnt)) |
| wlan_objmgr_pdev_obj_destroy(pdev); |
| |
| return; |
| } |
| EXPORT_SYMBOL(wlan_objmgr_pdev_release_ref); |