| /* |
| * Copyright (c) 2016-2019 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 <qdf_module.h> |
| #include "wlan_objmgr_global_obj_i.h" |
| #include "wlan_objmgr_psoc_obj_i.h" |
| #include "wlan_objmgr_pdev_obj_i.h" |
| #include "wlan_objmgr_vdev_obj_i.h" |
| |
| /** |
| ** APIs to Create/Delete Global object APIs |
| */ |
| |
| static QDF_STATUS wlan_objmgr_vdev_object_status( |
| struct wlan_objmgr_vdev *vdev) |
| { |
| uint8_t id; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| wlan_vdev_obj_lock(vdev); |
| |
| /* Iterate through all components to derive the object status */ |
| for (id = 0; id < WLAN_UMAC_MAX_COMPONENTS; id++) { |
| /* If component disabled, Ignore */ |
| if (vdev->obj_status[id] == QDF_STATUS_COMP_DISABLED) { |
| continue; |
| /* |
| * If component operates in Async, status is Partially created, |
| * break |
| */ |
| } else if (vdev->obj_status[id] == QDF_STATUS_COMP_ASYNC) { |
| if (!vdev->vdev_comp_priv_obj[id]) { |
| 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 ((vdev->obj_status[id] == QDF_STATUS_E_NOMEM) || |
| (vdev->obj_status[id] == QDF_STATUS_E_FAILURE)) { |
| status = QDF_STATUS_E_FAILURE; |
| break; |
| } |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| return status; |
| } |
| |
| static QDF_STATUS wlan_objmgr_vdev_obj_free(struct wlan_objmgr_vdev *vdev) |
| { |
| struct wlan_objmgr_pdev *pdev; |
| struct wlan_objmgr_psoc *psoc; |
| |
| if (!vdev) { |
| obj_mgr_err("vdev is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* if PDEV is NULL, return */ |
| pdev = wlan_vdev_get_pdev(vdev); |
| if (!pdev) { |
| obj_mgr_err("pdev is NULL for vdev-id: %d", |
| vdev->vdev_objmgr.vdev_id); |
| return QDF_STATUS_E_FAILURE; |
| } |
| psoc = wlan_pdev_get_psoc(pdev); |
| if (!psoc) { |
| obj_mgr_err("psoc is NULL in pdev"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| /* Detach VDEV from PDEV VDEV's list */ |
| if (wlan_objmgr_pdev_vdev_detach(pdev, vdev) == |
| QDF_STATUS_E_FAILURE) |
| return QDF_STATUS_E_FAILURE; |
| |
| /* Detach VDEV from PSOC VDEV's list */ |
| if (wlan_objmgr_psoc_vdev_detach(psoc, vdev) == |
| QDF_STATUS_E_FAILURE) |
| return QDF_STATUS_E_FAILURE; |
| |
| qdf_spinlock_destroy(&vdev->vdev_lock); |
| |
| qdf_mem_free(vdev->vdev_mlme.bss_chan); |
| qdf_mem_free(vdev->vdev_mlme.des_chan); |
| qdf_mem_free(vdev->vdev_nif.osdev); |
| qdf_mem_free(vdev); |
| |
| return QDF_STATUS_SUCCESS; |
| |
| } |
| |
| struct wlan_objmgr_vdev *wlan_objmgr_vdev_obj_create( |
| struct wlan_objmgr_pdev *pdev, |
| struct wlan_vdev_create_params *params) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_psoc *psoc; |
| uint8_t id; |
| wlan_objmgr_vdev_create_handler handler; |
| wlan_objmgr_vdev_status_handler stat_handler; |
| void *arg; |
| QDF_STATUS obj_status; |
| |
| if (!pdev) { |
| obj_mgr_err("pdev is NULL"); |
| return NULL; |
| } |
| psoc = wlan_pdev_get_psoc(pdev); |
| /* PSOC is NULL */ |
| if (!psoc) { |
| obj_mgr_err("psoc is NULL for pdev-id:%d", |
| pdev->pdev_objmgr.wlan_pdev_id); |
| return NULL; |
| } |
| /* Allocate vdev object memory */ |
| vdev = qdf_mem_malloc(sizeof(*vdev)); |
| if (!vdev) |
| return NULL; |
| |
| vdev->obj_state = WLAN_OBJ_STATE_ALLOCATED; |
| |
| vdev->vdev_mlme.bss_chan = qdf_mem_malloc(sizeof(struct wlan_channel)); |
| if (!vdev->vdev_mlme.bss_chan) { |
| qdf_mem_free(vdev); |
| return NULL; |
| } |
| |
| vdev->vdev_mlme.des_chan = qdf_mem_malloc(sizeof(struct wlan_channel)); |
| if (!vdev->vdev_mlme.des_chan) { |
| qdf_mem_free(vdev->vdev_mlme.bss_chan); |
| qdf_mem_free(vdev); |
| return NULL; |
| } |
| |
| /* Initialize spinlock */ |
| qdf_spinlock_create(&vdev->vdev_lock); |
| /* Attach VDEV to PSOC VDEV's list */ |
| if (wlan_objmgr_psoc_vdev_attach(psoc, vdev) != |
| QDF_STATUS_SUCCESS) { |
| obj_mgr_err("psoc vdev attach failed for vdev-id:%d", |
| vdev->vdev_objmgr.vdev_id); |
| qdf_mem_free(vdev->vdev_mlme.bss_chan); |
| qdf_mem_free(vdev->vdev_mlme.des_chan); |
| qdf_spinlock_destroy(&vdev->vdev_lock); |
| qdf_mem_free(vdev); |
| return NULL; |
| } |
| /* Store pdev in vdev */ |
| wlan_vdev_set_pdev(vdev, pdev); |
| /* Attach vdev to PDEV */ |
| if (wlan_objmgr_pdev_vdev_attach(pdev, vdev) != |
| QDF_STATUS_SUCCESS) { |
| obj_mgr_err("pdev vdev attach failed for vdev-id:%d", |
| vdev->vdev_objmgr.vdev_id); |
| wlan_objmgr_psoc_vdev_detach(psoc, vdev); |
| qdf_mem_free(vdev->vdev_mlme.bss_chan); |
| qdf_mem_free(vdev->vdev_mlme.des_chan); |
| qdf_spinlock_destroy(&vdev->vdev_lock); |
| qdf_mem_free(vdev); |
| return NULL; |
| } |
| /* set opmode */ |
| wlan_vdev_mlme_set_opmode(vdev, params->opmode); |
| /* set MAC address */ |
| wlan_vdev_mlme_set_macaddr(vdev, params->macaddr); |
| /* set MAT address */ |
| wlan_vdev_mlme_set_mataddr(vdev, params->mataddr); |
| /* Set create flags */ |
| vdev->vdev_objmgr.c_flags = params->flags; |
| /* store os-specific pointer */ |
| vdev->vdev_nif.osdev = params->osifp; |
| /* peer count to 0 */ |
| vdev->vdev_objmgr.wlan_peer_count = 0; |
| qdf_atomic_init(&vdev->vdev_objmgr.ref_cnt); |
| vdev->vdev_objmgr.print_cnt = 0; |
| wlan_objmgr_vdev_get_ref(vdev, WLAN_OBJMGR_ID); |
| /* Initialize max peer count based on opmode type */ |
| if (wlan_vdev_mlme_get_opmode(vdev) == QDF_STA_MODE) |
| vdev->vdev_objmgr.max_peer_count = WLAN_UMAC_MAX_STA_PEERS; |
| else |
| vdev->vdev_objmgr.max_peer_count = |
| wlan_pdev_get_max_peer_count(pdev); |
| |
| /* Initialize peer list */ |
| qdf_list_create(&vdev->vdev_objmgr.wlan_peer_list, |
| vdev->vdev_objmgr.max_peer_count + |
| WLAN_MAX_PDEV_TEMP_PEERS); |
| /* TODO init other parameters */ |
| |
| /* Invoke registered create handlers */ |
| for (id = 0; id < WLAN_UMAC_MAX_COMPONENTS; id++) { |
| handler = g_umac_glb_obj->vdev_create_handler[id]; |
| arg = g_umac_glb_obj->vdev_create_handler_arg[id]; |
| if (handler) |
| vdev->obj_status[id] = handler(vdev, arg); |
| else |
| vdev->obj_status[id] = QDF_STATUS_COMP_DISABLED; |
| } |
| |
| /* Derive object status */ |
| obj_status = wlan_objmgr_vdev_object_status(vdev); |
| |
| if (obj_status == QDF_STATUS_SUCCESS) { |
| /* Object status is SUCCESS, Object is created */ |
| vdev->obj_state = WLAN_OBJ_STATE_CREATED; |
| /* Invoke component registered status handlers */ |
| for (id = 0; id < WLAN_UMAC_MAX_COMPONENTS; id++) { |
| stat_handler = g_umac_glb_obj->vdev_status_handler[id]; |
| arg = g_umac_glb_obj->vdev_status_handler_arg[id]; |
| if (stat_handler) { |
| stat_handler(vdev, arg, |
| QDF_STATUS_SUCCESS); |
| } |
| } |
| /* |
| * Few components operates in Asynchrous communction, Object state |
| * partially created |
| */ |
| } else if (obj_status == QDF_STATUS_COMP_ASYNC) { |
| vdev->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 */ |
| wlan_objmgr_vdev_obj_delete(vdev); |
| obj_mgr_err("VDEV comp objects creation failed for vdev-id:%d", |
| vdev->vdev_objmgr.vdev_id); |
| return NULL; |
| } |
| |
| obj_mgr_info("Created vdev %d", vdev->vdev_objmgr.vdev_id); |
| |
| return vdev; |
| } |
| qdf_export_symbol(wlan_objmgr_vdev_obj_create); |
| |
| static QDF_STATUS wlan_objmgr_vdev_obj_destroy(struct wlan_objmgr_vdev *vdev) |
| { |
| uint8_t id; |
| wlan_objmgr_vdev_destroy_handler handler; |
| QDF_STATUS obj_status; |
| void *arg; |
| uint8_t vdev_id; |
| |
| if (!vdev) { |
| obj_mgr_err("vdev is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| wlan_objmgr_notify_destroy(vdev, WLAN_VDEV_OP); |
| |
| vdev_id = wlan_vdev_get_id(vdev); |
| |
| obj_mgr_debug("Physically deleting vdev %d", vdev_id); |
| |
| if (vdev->obj_state != WLAN_OBJ_STATE_LOGICALLY_DELETED) { |
| obj_mgr_err("VDEV object delete is not invoked vdevid:%d objstate:%d", |
| wlan_vdev_get_id(vdev), vdev->obj_state); |
| WLAN_OBJMGR_BUG(0); |
| } |
| |
| /* Invoke registered destroy handlers */ |
| for (id = 0; id < WLAN_UMAC_MAX_COMPONENTS; id++) { |
| handler = g_umac_glb_obj->vdev_destroy_handler[id]; |
| arg = g_umac_glb_obj->vdev_destroy_handler_arg[id]; |
| if (handler && |
| (vdev->obj_status[id] == QDF_STATUS_SUCCESS || |
| vdev->obj_status[id] == QDF_STATUS_COMP_ASYNC)) |
| vdev->obj_status[id] = handler(vdev, arg); |
| else |
| vdev->obj_status[id] = QDF_STATUS_COMP_DISABLED; |
| } |
| /* Derive object status */ |
| obj_status = wlan_objmgr_vdev_object_status(vdev); |
| |
| if (obj_status == QDF_STATUS_E_FAILURE) { |
| obj_mgr_err("VDEV object deletion failed: vdev-id: %d", |
| vdev_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) { |
| vdev->obj_state = WLAN_OBJ_STATE_PARTIALLY_DELETED; |
| return QDF_STATUS_COMP_ASYNC; |
| } |
| |
| /* Free VDEV object */ |
| return wlan_objmgr_vdev_obj_free(vdev); |
| } |
| |
| QDF_STATUS wlan_objmgr_vdev_obj_delete(struct wlan_objmgr_vdev *vdev) |
| { |
| uint8_t print_idx; |
| |
| if (!vdev) { |
| obj_mgr_err("vdev is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| obj_mgr_info("Logically deleting vdev %d", vdev->vdev_objmgr.vdev_id); |
| |
| print_idx = qdf_get_pidx(); |
| wlan_objmgr_print_ref_ids(vdev->vdev_objmgr.ref_id_dbg, |
| QDF_TRACE_LEVEL_DEBUG); |
| /* |
| * Update VDEV object state to LOGICALLY DELETED |
| * It prevents further access of this object |
| */ |
| wlan_vdev_obj_lock(vdev); |
| vdev->obj_state = WLAN_OBJ_STATE_LOGICALLY_DELETED; |
| wlan_vdev_obj_unlock(vdev); |
| wlan_objmgr_notify_log_delete(vdev, WLAN_VDEV_OP); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_OBJMGR_ID); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| qdf_export_symbol(wlan_objmgr_vdev_obj_delete); |
| |
| /** |
| ** APIs to attach/detach component objects |
| */ |
| QDF_STATUS wlan_objmgr_vdev_component_obj_attach( |
| struct wlan_objmgr_vdev *vdev, |
| enum wlan_umac_comp_id id, |
| void *comp_priv_obj, |
| QDF_STATUS status) |
| { |
| wlan_objmgr_vdev_status_handler stat_handler; |
| void *arg; |
| uint8_t i; |
| QDF_STATUS obj_status; |
| |
| /* component id is invalid */ |
| if (id >= WLAN_UMAC_MAX_COMPONENTS) |
| return QDF_STATUS_MAXCOMP_FAIL; |
| |
| wlan_vdev_obj_lock(vdev); |
| /* If there is a valid entry, return failure */ |
| if (vdev->vdev_comp_priv_obj[id]) { |
| wlan_vdev_obj_unlock(vdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* Save component's pointer and status */ |
| vdev->vdev_comp_priv_obj[id] = comp_priv_obj; |
| vdev->obj_status[id] = status; |
| wlan_vdev_obj_unlock(vdev); |
| if (vdev->obj_state != WLAN_OBJ_STATE_PARTIALLY_CREATED) |
| return QDF_STATUS_SUCCESS; |
| /* |
| * If VDEV 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_vdev_object_status(vdev); |
| /* STATUS_SUCCESS means, object is CREATED */ |
| if (obj_status == QDF_STATUS_SUCCESS) |
| vdev->obj_state = WLAN_OBJ_STATE_CREATED; |
| /* |
| * update state as CREATION failed, caller has to delete the |
| * VDEV object |
| */ |
| else if (obj_status == QDF_STATUS_E_FAILURE) |
| vdev->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)) { |
| for (i = 0; i < WLAN_UMAC_MAX_COMPONENTS; i++) { |
| stat_handler = g_umac_glb_obj->vdev_status_handler[i]; |
| arg = g_umac_glb_obj->vdev_status_handler_arg[i]; |
| if (stat_handler) |
| stat_handler(vdev, arg, obj_status); |
| } |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| qdf_export_symbol(wlan_objmgr_vdev_component_obj_attach); |
| |
| QDF_STATUS wlan_objmgr_vdev_component_obj_detach( |
| struct wlan_objmgr_vdev *vdev, |
| 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_vdev_obj_lock(vdev); |
| /* If there is a valid entry, return failure */ |
| if (vdev->vdev_comp_priv_obj[id] != comp_priv_obj) { |
| vdev->obj_status[id] = QDF_STATUS_E_FAILURE; |
| wlan_vdev_obj_unlock(vdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* Reset pointers to NULL, update the status*/ |
| vdev->vdev_comp_priv_obj[id] = NULL; |
| vdev->obj_status[id] = QDF_STATUS_SUCCESS; |
| wlan_vdev_obj_unlock(vdev); |
| |
| /** |
| *If VDEV object status is partially destroyed means, this API is |
| * invoked with differnt context, this block should be executed for |
| * async components only |
| */ |
| if ((vdev->obj_state == WLAN_OBJ_STATE_PARTIALLY_DELETED) || |
| (vdev->obj_state == WLAN_OBJ_STATE_COMP_DEL_PROGRESS)) { |
| /* Derive object status */ |
| obj_status = wlan_objmgr_vdev_object_status(vdev); |
| if (obj_status == QDF_STATUS_SUCCESS) { |
| /* |
| * Update the status as Deleted, if full object |
| * deletion is in progress |
| */ |
| if (vdev->obj_state == WLAN_OBJ_STATE_PARTIALLY_DELETED) |
| vdev->obj_state = WLAN_OBJ_STATE_DELETED; |
| /* |
| * Move to creation state, since this component |
| * deletion alone requested |
| */ |
| else if (vdev->obj_state == |
| WLAN_OBJ_STATE_COMP_DEL_PROGRESS) |
| vdev->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 (vdev->obj_state == WLAN_OBJ_STATE_PARTIALLY_DELETED) |
| vdev->obj_state = |
| WLAN_OBJ_STATE_DELETION_FAILED; |
| /* Move to creation state, since this component |
| deletion alone requested (do not block other |
| components) */ |
| else if (vdev->obj_state == |
| WLAN_OBJ_STATE_COMP_DEL_PROGRESS) |
| vdev->obj_state = WLAN_OBJ_STATE_CREATED; |
| } |
| /* Delete vdev object */ |
| if ((obj_status == QDF_STATUS_SUCCESS) && |
| (vdev->obj_state == WLAN_OBJ_STATE_DELETED)) { |
| /* Free VDEV object */ |
| return wlan_objmgr_vdev_obj_free(vdev); |
| } |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| qdf_export_symbol(wlan_objmgr_vdev_component_obj_detach); |
| |
| /** |
| ** APIs to operations on vdev objects |
| */ |
| QDF_STATUS wlan_objmgr_iterate_peerobj_list( |
| struct wlan_objmgr_vdev *vdev, |
| wlan_objmgr_vdev_op_handler handler, |
| void *arg, 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; |
| uint8_t vdev_id; |
| |
| if (!vdev) { |
| obj_mgr_err("VDEV is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| wlan_vdev_obj_lock(vdev); |
| vdev_id = wlan_vdev_get_id(vdev); |
| |
| if (vdev->obj_state != WLAN_OBJ_STATE_CREATED) { |
| wlan_vdev_obj_unlock(vdev); |
| obj_mgr_err("VDEV is not in create state(:%d): vdev-id:%d", |
| vdev_id, vdev->obj_state); |
| return QDF_STATUS_E_FAILURE; |
| } |
| wlan_objmgr_vdev_get_ref(vdev, dbg_id); |
| peer_list = &vdev->vdev_objmgr.wlan_peer_list; |
| if (peer_list) { |
| /* Iterate through VDEV's peer list */ |
| peer = wlan_vdev_peer_list_peek_head(peer_list); |
| while (peer) { |
| peer_next = wlan_peer_get_next_peer_of_vdev(peer_list, |
| peer); |
| if (wlan_objmgr_peer_try_get_ref(peer, dbg_id) == |
| QDF_STATUS_SUCCESS) { |
| /* Invoke handler for operation */ |
| handler(vdev, (void *)peer, arg); |
| wlan_objmgr_peer_release_ref(peer, dbg_id); |
| } |
| peer = peer_next; |
| } |
| } |
| wlan_objmgr_vdev_release_ref(vdev, dbg_id); |
| wlan_vdev_obj_unlock(vdev); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_obj_vdev_populate_logically_del_peerlist() - get peer |
| * from vdev peer list |
| * @obj_list: peer object list |
| * @vdev_obj: vdev object mgr substructure |
| * @dbg_id: id of the caller |
| * |
| * API to finds peer object pointer by vdev from peer hash list for a node |
| * which is in logically deleted state |
| * |
| * Caller to free the list allocated in this function |
| * |
| * Return: list of peer pointers |
| * NULL on FAILURE |
| */ |
| static qdf_list_t *wlan_obj_vdev_populate_logically_del_peerlist( |
| qdf_list_t *obj_list, |
| struct wlan_objmgr_vdev_objmgr *vdev_obj, |
| wlan_objmgr_ref_dbgid dbg_id) |
| { |
| struct wlan_objmgr_peer *peer; |
| struct wlan_objmgr_peer *peer_next; |
| struct wlan_logically_del_peer *peer_list; |
| qdf_list_t *logical_del_peerlist; |
| bool lock_released = false; |
| |
| logical_del_peerlist = qdf_mem_malloc(sizeof(*logical_del_peerlist)); |
| if (!logical_del_peerlist) |
| return NULL; |
| |
| qdf_list_create(logical_del_peerlist, vdev_obj->max_peer_count); |
| |
| peer = wlan_vdev_peer_list_peek_head(obj_list); |
| while (peer) { |
| wlan_peer_obj_lock(peer); |
| peer_next = wlan_peer_get_next_peer_of_vdev(obj_list, peer); |
| if (peer->obj_state == WLAN_OBJ_STATE_LOGICALLY_DELETED && |
| qdf_atomic_read(&peer->peer_objmgr.ref_cnt)) { |
| wlan_objmgr_peer_get_ref(peer, dbg_id); |
| wlan_peer_obj_unlock(peer); |
| lock_released = true; |
| |
| peer_list = qdf_mem_malloc(sizeof(*peer_list)); |
| if (!peer_list) { |
| wlan_objmgr_peer_release_ref(peer, dbg_id); |
| WLAN_OBJMGR_BUG(0); |
| break; |
| } |
| |
| peer_list->peer = peer; |
| qdf_list_insert_front(logical_del_peerlist, |
| &peer_list->list); |
| } |
| |
| if (!lock_released) |
| wlan_peer_obj_unlock(peer); |
| |
| peer = peer_next; |
| lock_released = false; |
| } |
| |
| /* Not found, return NULL */ |
| if (qdf_list_empty(logical_del_peerlist)) { |
| qdf_mem_free(logical_del_peerlist); |
| return NULL; |
| } |
| |
| return logical_del_peerlist; |
| } |
| |
| qdf_list_t *wlan_objmgr_vdev_get_log_del_peer_list( |
| struct wlan_objmgr_vdev *vdev, |
| wlan_objmgr_ref_dbgid dbg_id) |
| { |
| qdf_list_t *peer_list; |
| qdf_list_t *log_del_peer_list = NULL; |
| |
| if (vdev->obj_state != WLAN_OBJ_STATE_CREATED) { |
| obj_mgr_err("Invalid state vdev:%d state:%d", |
| wlan_vdev_get_id(vdev), vdev->obj_state); |
| return NULL; |
| } |
| |
| wlan_vdev_obj_lock(vdev); |
| if (vdev->vdev_objmgr.wlan_peer_count == 0) { |
| wlan_vdev_obj_unlock(vdev); |
| return NULL; |
| } |
| |
| wlan_objmgr_vdev_get_ref(vdev, dbg_id); |
| peer_list = &vdev->vdev_objmgr.wlan_peer_list; |
| if (peer_list) { |
| log_del_peer_list = |
| wlan_obj_vdev_populate_logically_del_peerlist( |
| peer_list, &vdev->vdev_objmgr, |
| dbg_id); |
| } |
| |
| wlan_objmgr_vdev_release_ref(vdev, dbg_id); |
| wlan_vdev_obj_unlock(vdev); |
| |
| return log_del_peer_list; |
| } |
| |
| QDF_STATUS wlan_objmgr_trigger_vdev_comp_priv_object_creation( |
| struct wlan_objmgr_vdev *vdev, |
| enum wlan_umac_comp_id id) |
| { |
| wlan_objmgr_vdev_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_vdev_obj_lock(vdev); |
| /* |
| * If component object is already created, delete old |
| * component object, then invoke creation |
| */ |
| if (vdev->vdev_comp_priv_obj[id]) { |
| wlan_vdev_obj_unlock(vdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| /* Invoke registered create handlers */ |
| handler = g_umac_glb_obj->vdev_create_handler[id]; |
| arg = g_umac_glb_obj->vdev_create_handler_arg[id]; |
| if (handler) |
| vdev->obj_status[id] = handler(vdev, arg); |
| else |
| return QDF_STATUS_E_FAILURE; |
| |
| /* If object status is created, then only handle this object status */ |
| if (vdev->obj_state == WLAN_OBJ_STATE_CREATED) { |
| /* Derive object status */ |
| obj_status = wlan_objmgr_vdev_object_status(vdev); |
| /* Move PDEV object state to Partially created state */ |
| if (obj_status == QDF_STATUS_COMP_ASYNC) { |
| /*TODO atomic */ |
| vdev->obj_state = WLAN_OBJ_STATE_PARTIALLY_CREATED; |
| } |
| } |
| return obj_status; |
| } |
| |
| QDF_STATUS wlan_objmgr_trigger_vdev_comp_priv_object_deletion( |
| struct wlan_objmgr_vdev *vdev, |
| enum wlan_umac_comp_id id) |
| { |
| wlan_objmgr_vdev_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_vdev_obj_lock(vdev); |
| /* Component object was never created, invalid operation */ |
| if (!vdev->vdev_comp_priv_obj[id]) { |
| wlan_vdev_obj_unlock(vdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| /* Invoke registered create handlers */ |
| handler = g_umac_glb_obj->vdev_destroy_handler[id]; |
| arg = g_umac_glb_obj->vdev_destroy_handler_arg[id]; |
| if (handler) |
| vdev->obj_status[id] = handler(vdev, arg); |
| else |
| return QDF_STATUS_E_FAILURE; |
| |
| /* If object status is created, then only handle this object status */ |
| if (vdev->obj_state == WLAN_OBJ_STATE_CREATED) { |
| obj_status = wlan_objmgr_vdev_object_status(vdev); |
| /* move object state to DEL progress */ |
| if (obj_status == QDF_STATUS_COMP_ASYNC) |
| vdev->obj_state = WLAN_OBJ_STATE_COMP_DEL_PROGRESS; |
| } |
| return obj_status; |
| } |
| |
| |
| |
| static void wlan_obj_vdev_peerlist_add_tail(qdf_list_t *obj_list, |
| struct wlan_objmgr_peer *obj) |
| { |
| qdf_list_insert_back(obj_list, &obj->vdev_peer); |
| } |
| |
| static QDF_STATUS wlan_obj_vdev_peerlist_remove_peer(qdf_list_t *obj_list, |
| struct wlan_objmgr_peer *peer) |
| { |
| qdf_list_node_t *vdev_node = NULL; |
| |
| if (!peer) |
| return QDF_STATUS_E_FAILURE; |
| /* get vdev list node element */ |
| vdev_node = &peer->vdev_peer; |
| /* 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_vdev_peer_attach(struct wlan_objmgr_vdev *vdev, |
| struct wlan_objmgr_peer *peer) |
| { |
| struct wlan_objmgr_vdev_objmgr *objmgr = &vdev->vdev_objmgr; |
| struct wlan_objmgr_pdev *pdev; |
| enum QDF_OPMODE opmode; |
| |
| wlan_vdev_obj_lock(vdev); |
| pdev = wlan_vdev_get_pdev(vdev); |
| /* If Max VDEV peer count exceeds, return failure */ |
| if (peer->peer_mlme.peer_type != WLAN_PEER_STA_TEMP) { |
| if (objmgr->wlan_peer_count >= objmgr->max_peer_count) { |
| wlan_vdev_obj_unlock(vdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| /* If Max PDEV peer count exceeds, return failure */ |
| wlan_pdev_obj_lock(pdev); |
| if (peer->peer_mlme.peer_type == WLAN_PEER_STA_TEMP) { |
| if (wlan_pdev_get_temp_peer_count(pdev) >= |
| WLAN_MAX_PDEV_TEMP_PEERS) { |
| wlan_pdev_obj_unlock(pdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } else { |
| if (wlan_pdev_get_peer_count(pdev) >= |
| wlan_pdev_get_max_peer_count(pdev)) { |
| wlan_pdev_obj_unlock(pdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| } |
| |
| if (peer->peer_mlme.peer_type == WLAN_PEER_STA_TEMP) |
| wlan_pdev_incr_temp_peer_count(wlan_vdev_get_pdev(vdev)); |
| else |
| wlan_pdev_incr_peer_count(wlan_vdev_get_pdev(vdev)); |
| wlan_pdev_obj_unlock(pdev); |
| |
| wlan_vdev_obj_lock(vdev); |
| /* Add peer to vdev's peer list */ |
| wlan_obj_vdev_peerlist_add_tail(&objmgr->wlan_peer_list, peer); |
| objmgr->wlan_peer_count++; |
| |
| if (WLAN_ADDR_EQ(wlan_peer_get_macaddr(peer), |
| wlan_vdev_mlme_get_macaddr(vdev)) == |
| QDF_STATUS_SUCCESS) { |
| /* |
| * if peer mac address and vdev mac address match, set |
| * this peer as self peer |
| */ |
| wlan_vdev_set_selfpeer(vdev, peer); |
| opmode = wlan_vdev_mlme_get_opmode(vdev); |
| /* For AP mode, self peer and BSS peer are same */ |
| if ((opmode == QDF_SAP_MODE) || (opmode == QDF_P2P_GO_MODE)) |
| wlan_vdev_set_bsspeer(vdev, peer); |
| } |
| /* set BSS peer for sta */ |
| if ((wlan_vdev_mlme_get_opmode(vdev) == QDF_STA_MODE || |
| wlan_vdev_mlme_get_opmode(vdev) == QDF_P2P_CLIENT_MODE) && |
| (wlan_peer_get_peer_type(peer) == WLAN_PEER_AP || |
| wlan_peer_get_peer_type(peer) == WLAN_PEER_P2P_GO)) |
| wlan_vdev_set_bsspeer(vdev, peer); |
| |
| /* Increment vdev ref count to make sure it won't be destroyed before */ |
| wlan_objmgr_vdev_get_ref(vdev, WLAN_OBJMGR_ID); |
| wlan_vdev_obj_unlock(vdev); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS wlan_objmgr_vdev_peer_detach(struct wlan_objmgr_vdev *vdev, |
| struct wlan_objmgr_peer *peer) |
| { |
| struct wlan_objmgr_vdev_objmgr *objmgr = &vdev->vdev_objmgr; |
| struct wlan_objmgr_pdev *pdev; |
| |
| wlan_vdev_obj_lock(vdev); |
| /* if peer count is 0, return failure */ |
| if (objmgr->wlan_peer_count == 0) { |
| wlan_vdev_obj_unlock(vdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if ((wlan_peer_get_peer_type(peer) == WLAN_PEER_AP) || |
| (wlan_peer_get_peer_type(peer) == WLAN_PEER_P2P_GO)) { |
| if (wlan_vdev_get_selfpeer(vdev) == peer) { |
| /* |
| * There might be instances where new node is created |
| * before deleting existing node, in which case selfpeer |
| * will be pointing to the new node. So set selfpeer to |
| * NULL only if vdev->vdev_objmgr.self_peer is pointing |
| * to the peer processed for deletion |
| */ |
| wlan_vdev_set_selfpeer(vdev, NULL); |
| } |
| |
| if (wlan_vdev_get_bsspeer(vdev) == peer) { |
| /* |
| * There might be instances where new node is created |
| * before deleting existing node, in which case bsspeer |
| * in vdev will be pointing to the new node. So set |
| * bsspeer to NULL only if vdev->vdev_objmgr.bss_peer is |
| * pointing to the peer processed for deletion |
| */ |
| wlan_vdev_set_bsspeer(vdev, NULL); |
| } |
| } |
| |
| /* remove peer from vdev's peer list */ |
| if (wlan_obj_vdev_peerlist_remove_peer(&objmgr->wlan_peer_list, peer) |
| == QDF_STATUS_E_FAILURE) { |
| wlan_vdev_obj_unlock(vdev); |
| return QDF_STATUS_E_FAILURE; |
| } |
| /* decrement peer count */ |
| objmgr->wlan_peer_count--; |
| /* decrement pdev peer count */ |
| pdev = wlan_vdev_get_pdev(vdev); |
| wlan_vdev_obj_unlock(vdev); |
| |
| wlan_pdev_obj_lock(pdev); |
| if (peer->peer_mlme.peer_type == WLAN_PEER_STA_TEMP) |
| wlan_pdev_decr_temp_peer_count(pdev); |
| else |
| wlan_pdev_decr_peer_count(pdev); |
| wlan_pdev_obj_unlock(pdev); |
| |
| /* decrement vdev ref count after peer released its reference */ |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_OBJMGR_ID); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| struct wlan_objmgr_peer *wlan_objmgr_vdev_try_get_bsspeer( |
| struct wlan_objmgr_vdev *vdev, |
| wlan_objmgr_ref_dbgid id) |
| { |
| struct wlan_objmgr_peer *peer; |
| QDF_STATUS status = QDF_STATUS_E_EMPTY; |
| |
| if (!vdev) |
| return NULL; |
| |
| wlan_vdev_obj_lock(vdev); |
| peer = wlan_vdev_get_bsspeer(vdev); |
| if (peer) |
| status = wlan_objmgr_peer_try_get_ref(peer, id); |
| wlan_vdev_obj_unlock(vdev); |
| |
| if (QDF_IS_STATUS_SUCCESS(status)) |
| return peer; |
| |
| return NULL; |
| } |
| |
| void *wlan_objmgr_vdev_get_comp_private_obj( |
| struct wlan_objmgr_vdev *vdev, |
| 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 (!vdev) { |
| QDF_BUG(0); |
| return NULL; |
| } |
| |
| comp_priv_obj = vdev->vdev_comp_priv_obj[id]; |
| |
| return comp_priv_obj; |
| } |
| qdf_export_symbol(wlan_objmgr_vdev_get_comp_private_obj); |
| |
| void wlan_objmgr_vdev_get_ref(struct wlan_objmgr_vdev *vdev, |
| wlan_objmgr_ref_dbgid id) |
| { |
| if (!vdev) { |
| obj_mgr_err("vdev obj is NULL for id:%d", id); |
| QDF_ASSERT(0); |
| return; |
| } |
| /* Increment ref count */ |
| qdf_atomic_inc(&vdev->vdev_objmgr.ref_cnt); |
| qdf_atomic_inc(&vdev->vdev_objmgr.ref_id_dbg[id]); |
| |
| return; |
| } |
| qdf_export_symbol(wlan_objmgr_vdev_get_ref); |
| |
| QDF_STATUS wlan_objmgr_vdev_try_get_ref(struct wlan_objmgr_vdev *vdev, |
| wlan_objmgr_ref_dbgid id) |
| { |
| uint8_t vdev_id; |
| |
| if (!vdev) { |
| obj_mgr_err("vdev obj is NULL for id:%d", id); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| wlan_vdev_obj_lock(vdev); |
| vdev_id = wlan_vdev_get_id(vdev); |
| if (vdev->obj_state != WLAN_OBJ_STATE_CREATED) { |
| wlan_vdev_obj_unlock(vdev); |
| if (vdev->vdev_objmgr.print_cnt++ <= |
| WLAN_OBJMGR_RATELIMIT_THRESH) |
| obj_mgr_err( |
| "[Ref id: %d] vdev(%d) is not in Created state(%d)", |
| id, vdev_id, vdev->obj_state); |
| |
| return QDF_STATUS_E_RESOURCES; |
| } |
| |
| /* Increment ref count */ |
| wlan_objmgr_vdev_get_ref(vdev, id); |
| wlan_vdev_obj_unlock(vdev); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| qdf_export_symbol(wlan_objmgr_vdev_try_get_ref); |
| |
| void wlan_objmgr_vdev_release_ref(struct wlan_objmgr_vdev *vdev, |
| wlan_objmgr_ref_dbgid id) |
| { |
| uint8_t vdev_id; |
| |
| if (!vdev) { |
| obj_mgr_err("vdev obj is NULL for id:%d", id); |
| QDF_ASSERT(0); |
| return; |
| } |
| |
| vdev_id = wlan_vdev_get_id(vdev); |
| |
| if (!qdf_atomic_read(&vdev->vdev_objmgr.ref_id_dbg[id])) { |
| obj_mgr_err("vdev (id:%d)ref cnt was not taken by %d", |
| vdev_id, id); |
| wlan_objmgr_print_ref_ids(vdev->vdev_objmgr.ref_id_dbg, |
| QDF_TRACE_LEVEL_FATAL); |
| WLAN_OBJMGR_BUG(0); |
| return; |
| } |
| |
| if (!qdf_atomic_read(&vdev->vdev_objmgr.ref_cnt)) { |
| obj_mgr_err("vdev ref cnt is 0"); |
| WLAN_OBJMGR_BUG(0); |
| return; |
| } |
| qdf_atomic_dec(&vdev->vdev_objmgr.ref_id_dbg[id]); |
| |
| /* Decrement ref count, free vdev, if ref count == 0 */ |
| if (qdf_atomic_dec_and_test(&vdev->vdev_objmgr.ref_cnt)) |
| wlan_objmgr_vdev_obj_destroy(vdev); |
| |
| return; |
| } |
| qdf_export_symbol(wlan_objmgr_vdev_release_ref); |
| |
| struct wlan_objmgr_vdev *wlan_pdev_vdev_list_peek_active_head( |
| struct wlan_objmgr_pdev *pdev, |
| qdf_list_t *vdev_list, wlan_objmgr_ref_dbgid dbg_id) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| qdf_list_node_t *node = NULL; |
| qdf_list_node_t *prev_node = NULL; |
| |
| wlan_pdev_obj_lock(pdev); |
| |
| if (qdf_list_peek_front(vdev_list, &node) != QDF_STATUS_SUCCESS) { |
| wlan_pdev_obj_unlock(pdev); |
| return NULL; |
| } |
| |
| do { |
| vdev = qdf_container_of(node, struct wlan_objmgr_vdev, |
| vdev_node); |
| if (wlan_objmgr_vdev_try_get_ref(vdev, dbg_id) == |
| QDF_STATUS_SUCCESS) { |
| wlan_pdev_obj_unlock(pdev); |
| return vdev; |
| } |
| |
| prev_node = node; |
| } while (qdf_list_peek_next(vdev_list, prev_node, &node) == |
| QDF_STATUS_SUCCESS); |
| |
| wlan_pdev_obj_unlock(pdev); |
| |
| return NULL; |
| } |
| |
| struct wlan_objmgr_vdev *wlan_vdev_get_next_active_vdev_of_pdev( |
| struct wlan_objmgr_pdev *pdev, |
| qdf_list_t *vdev_list, |
| struct wlan_objmgr_vdev *vdev, |
| wlan_objmgr_ref_dbgid dbg_id) |
| { |
| struct wlan_objmgr_vdev *vdev_next; |
| qdf_list_node_t *node = &vdev->vdev_node; |
| qdf_list_node_t *prev_node = NULL; |
| |
| if (!node) |
| return NULL; |
| |
| wlan_pdev_obj_lock(pdev); |
| prev_node = node; |
| while (qdf_list_peek_next(vdev_list, prev_node, &node) == |
| QDF_STATUS_SUCCESS) { |
| vdev_next = qdf_container_of(node, struct wlan_objmgr_vdev, |
| vdev_node); |
| if (wlan_objmgr_vdev_try_get_ref(vdev_next, dbg_id) == |
| QDF_STATUS_SUCCESS) { |
| wlan_pdev_obj_unlock(pdev); |
| return vdev_next; |
| } |
| |
| prev_node = node; |
| } |
| wlan_pdev_obj_unlock(pdev); |
| |
| return NULL; |
| } |
| |