/* * Copyright (c) 2021, The Linux Foundation. All rights reserved. * Copyright (c) 2021-2023 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 * 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. */ #include <wlan_mlo_mgr_cmn.h> #include <wlan_mlo_mgr_public_structs.h> #include "wlan_mlo_mgr_main.h" #include "qdf_module.h" #include "qdf_types.h" #include "wlan_cmn.h" #include "wlan_mlo_mgr_peer.h" struct aid_search { struct wlan_mlo_peer_context *ml_peer; uint16_t aid; }; struct mlpeerid_search { struct wlan_mlo_peer_context *ml_peer; uint16_t ml_peerid; }; struct mac_addr_search { struct wlan_mlo_peer_context *ml_peer; struct qdf_mac_addr mac_addr; }; static inline struct wlan_mlo_peer_context *wlan_mlo_peer_list_peek_head( qdf_list_t *peer_list) { struct wlan_mlo_peer_context *ml_peer; qdf_list_node_t *peer_node = NULL; /* This API is invoked with lock acquired, do not add log prints */ if (qdf_list_peek_front(peer_list, &peer_node) != QDF_STATUS_SUCCESS) return NULL; ml_peer = qdf_container_of(peer_node, struct wlan_mlo_peer_context, peer_node); return ml_peer; } static inline struct wlan_mlo_peer_context *wlan_mlo_peer_get_next_mlpeer( qdf_list_t *peer_list, struct wlan_mlo_peer_context *ml_peer) { struct wlan_mlo_peer_context *next_peer; qdf_list_node_t *node = &ml_peer->peer_node; qdf_list_node_t *next_node = NULL; /* This API is invoked with lock acquired, do not add log prints */ if (!node) return NULL; if (qdf_list_peek_next(peer_list, node, &next_node) != QDF_STATUS_SUCCESS) return NULL; next_peer = qdf_container_of(next_node, struct wlan_mlo_peer_context, peer_node); return next_peer; } struct wlan_mlo_peer_context *mlo_get_mlpeer( struct wlan_mlo_dev_context *ml_dev, const struct qdf_mac_addr *ml_addr) { uint8_t hash_index; struct wlan_mlo_peer_list *mlo_peer_list; struct wlan_mlo_peer_context *ml_peer; struct wlan_mlo_peer_context *next_ml_peer; qdf_list_t *peer_hash_list; mlo_peer_list = &ml_dev->mlo_peer_list; hash_index = WLAN_PEER_HASH(ml_addr->bytes); peer_hash_list = &mlo_peer_list->peer_hash[hash_index]; /* Get first vdev */ ml_peer = wlan_mlo_peer_list_peek_head(peer_hash_list); /** * Iterate through pdev's vdev list, till vdev id matches with * entry of vdev list */ while (ml_peer) { if (qdf_is_macaddr_equal(&ml_peer->peer_mld_addr, ml_addr)) return ml_peer; /* get next vdev */ next_ml_peer = wlan_mlo_peer_get_next_mlpeer(peer_hash_list, ml_peer); ml_peer = next_ml_peer; } return NULL; } QDF_STATUS wlan_mlo_iterate_ml_peerlist(struct wlan_mlo_dev_context *ml_dev, wlan_mlo_op_handler handler, void *arg) { uint8_t hash_index; struct wlan_mlo_peer_list *peerlist; struct wlan_mlo_peer_context *ml_peer; struct wlan_mlo_peer_context *next; qdf_list_t *peer_hash_list; QDF_STATUS status; peerlist = &ml_dev->mlo_peer_list; ml_peerlist_lock_acquire(peerlist); for (hash_index = 0; hash_index < WLAN_PEER_HASHSIZE; hash_index++) { peer_hash_list = &peerlist->peer_hash[hash_index]; /* Get first vdev */ ml_peer = wlan_mlo_peer_list_peek_head(peer_hash_list); /** * Iterate through pdev's vdev list, till vdev id matches with * entry of vdev list */ while (ml_peer) { status = handler(ml_dev, ml_peer, arg); if (status == QDF_STATUS_SUCCESS) { ml_peerlist_lock_release(peerlist); return QDF_STATUS_SUCCESS; } /* get next ml peer */ next = wlan_mlo_peer_get_next_mlpeer(peer_hash_list, ml_peer); ml_peer = next; } } ml_peerlist_lock_release(peerlist); return QDF_STATUS_E_NOENT; } static QDF_STATUS wlan_find_mlpeer_link_mac_addr(struct wlan_mlo_dev_context *ml_dev, void *iter_ml_peer, void *arg) { struct mac_addr_search *link_mac_arg = (struct mac_addr_search *)arg; struct wlan_mlo_link_peer_entry *link_peer; struct wlan_mlo_peer_context *ml_peer; uint8_t i; ml_peer = (struct wlan_mlo_peer_context *)iter_ml_peer; mlo_debug("MLD ID %d ML Peer mac " QDF_MAC_ADDR_FMT, ml_dev->mld_id, QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { link_peer = &ml_peer->peer_list[i]; mlo_debug("MLD ID %d, index %d ML Peer exists with mac " QDF_MAC_ADDR_FMT, i, ml_dev->mld_id, QDF_MAC_ADDR_REF(link_peer->link_addr.bytes)); if (qdf_is_macaddr_equal(&link_mac_arg->mac_addr, &link_peer->link_addr)) { link_mac_arg->ml_peer = ml_peer; return QDF_STATUS_SUCCESS; } } return QDF_STATUS_E_NOENT; } static QDF_STATUS wlan_find_mlpeer_mld_mac_addr(struct wlan_mlo_dev_context *ml_dev, void *iter_ml_peer, void *arg) { struct mac_addr_search *mld_mac_arg = (struct mac_addr_search *)arg; struct wlan_mlo_peer_context *ml_peer; ml_peer = (struct wlan_mlo_peer_context *)iter_ml_peer; mlo_debug("MLD ID %d ML Peer mac " QDF_MAC_ADDR_FMT, ml_dev->mld_id, QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); if (qdf_is_macaddr_equal(&mld_mac_arg->mac_addr, &ml_peer->peer_mld_addr)) { mld_mac_arg->ml_peer = ml_peer; return QDF_STATUS_SUCCESS; } return QDF_STATUS_E_NOENT; } static QDF_STATUS wlan_find_mlpeer_aid(struct wlan_mlo_dev_context *ml_dev, void *iter_ml_peer, void *arg) { struct aid_search *aid_arg = (struct aid_search *)arg; struct wlan_mlo_peer_context *ml_peer; ml_peer = (struct wlan_mlo_peer_context *)iter_ml_peer; if (aid_arg->aid == ml_peer->assoc_id) { aid_arg->ml_peer = ml_peer; return QDF_STATUS_SUCCESS; } return QDF_STATUS_E_NOENT; } static QDF_STATUS wlan_find_mlpeer_ml_peerid(struct wlan_mlo_dev_context *ml_dev, void *iter_ml_peer, void *arg) { struct mlpeerid_search *mlpeer_id_arg = (struct mlpeerid_search *)arg; struct wlan_mlo_peer_context *ml_peer; ml_peer = (struct wlan_mlo_peer_context *)iter_ml_peer; if (mlpeer_id_arg->ml_peerid == ml_peer->mlo_peer_id) { mlpeer_id_arg->ml_peer = ml_peer; return QDF_STATUS_SUCCESS; } return QDF_STATUS_E_NOENT; } struct wlan_mlo_peer_context *wlan_mlo_get_mlpeer_by_linkmac( struct wlan_mlo_dev_context *ml_dev, struct qdf_mac_addr *link_mac) { struct mac_addr_search link_mac_arg; QDF_STATUS status; mlo_debug("MLD ID %d ML Peer search with link mac " QDF_MAC_ADDR_FMT, ml_dev->mld_id, QDF_MAC_ADDR_REF(link_mac->bytes)); qdf_copy_macaddr(&link_mac_arg.mac_addr, link_mac); status = wlan_mlo_iterate_ml_peerlist(ml_dev, wlan_find_mlpeer_link_mac_addr, &link_mac_arg); if (status == QDF_STATUS_SUCCESS) return link_mac_arg.ml_peer; /* TODO: Take ref */ return NULL; } qdf_export_symbol(wlan_mlo_get_mlpeer_by_linkmac); struct wlan_mlo_peer_context *wlan_mlo_get_mlpeer_by_aid( struct wlan_mlo_dev_context *ml_dev, uint16_t assoc_id) { struct aid_search aid_arg; QDF_STATUS status; aid_arg.aid = assoc_id; status = wlan_mlo_iterate_ml_peerlist(ml_dev, wlan_find_mlpeer_aid, &aid_arg); if (status == QDF_STATUS_SUCCESS) return aid_arg.ml_peer; /* TODO: Take ref */ return NULL; } struct wlan_mlo_peer_context *wlan_mlo_get_mlpeer_by_mld_mac( struct wlan_mlo_dev_context *ml_dev, struct qdf_mac_addr *mld_mac) { struct mac_addr_search mld_mac_arg; QDF_STATUS status; mlo_debug("MLD ID %d ML Peer search with mld mac " QDF_MAC_ADDR_FMT, ml_dev->mld_id, QDF_MAC_ADDR_REF(mld_mac->bytes)); qdf_copy_macaddr(&mld_mac_arg.mac_addr, mld_mac); status = wlan_mlo_iterate_ml_peerlist(ml_dev, wlan_find_mlpeer_mld_mac_addr, &mld_mac_arg); if (QDF_IS_STATUS_SUCCESS(status)) return mld_mac_arg.ml_peer; /* TODO: Take ref */ return NULL; } qdf_export_symbol(wlan_mlo_get_mlpeer_by_mld_mac); struct wlan_mlo_peer_context *wlan_mlo_get_mlpeer_by_peer_mladdr(struct qdf_mac_addr *mldaddr, struct wlan_mlo_dev_context **mldev) { struct wlan_mlo_dev_context *mld_cur; struct wlan_mlo_dev_context *mld_next; struct wlan_mlo_peer_context *ml_peer; qdf_list_t *ml_list; struct mlo_mgr_context *mlo_mgr_ctx = wlan_objmgr_get_mlo_ctx(); if (!mlo_mgr_ctx) return NULL; ml_link_lock_acquire(mlo_mgr_ctx); ml_list = &mlo_mgr_ctx->ml_dev_list; mld_cur = wlan_mlo_list_peek_head(ml_list); while (mld_cur) { ml_peer = mlo_get_mlpeer(mld_cur, mldaddr); if (ml_peer != NULL) { *mldev = mld_cur; ml_link_lock_release(mlo_mgr_ctx); return ml_peer; } mld_next = wlan_mlo_get_next_mld_ctx(ml_list, mld_cur); mld_cur = mld_next; } ml_link_lock_release(mlo_mgr_ctx); return NULL; } struct wlan_mlo_peer_context *wlan_mlo_get_mlpeer_by_ml_peerid( struct wlan_mlo_dev_context *ml_dev, uint16_t ml_peerid) { struct mlpeerid_search peerid_arg; QDF_STATUS status; peerid_arg.ml_peerid = ml_peerid; status = wlan_mlo_iterate_ml_peerlist(ml_dev, wlan_find_mlpeer_ml_peerid, &peerid_arg); if (status == QDF_STATUS_SUCCESS) return peerid_arg.ml_peer; /* TODO: Take ref */ return NULL; } qdf_export_symbol(wlan_mlo_get_mlpeer_by_ml_peerid); struct wlan_mlo_peer_context *wlan_mlo_get_mlpeer( struct wlan_mlo_dev_context *ml_dev, struct qdf_mac_addr *ml_addr) { struct wlan_mlo_peer_context *ml_peer; struct wlan_mlo_peer_list *mlo_peer_list; mlo_debug("MLD ID %d ML Peer search mac " QDF_MAC_ADDR_FMT, ml_dev->mld_id, QDF_MAC_ADDR_REF(ml_addr->bytes)); mlo_peer_list = &ml_dev->mlo_peer_list; ml_peerlist_lock_acquire(mlo_peer_list); ml_peer = mlo_get_mlpeer(ml_dev, ml_addr); if (!ml_peer) { ml_peerlist_lock_release(mlo_peer_list); return NULL; } /* TODO: Take ref */ ml_peerlist_lock_release(mlo_peer_list); return ml_peer; } static void wlan_mlo_peerlist_add_tail(qdf_list_t *obj_list, struct wlan_mlo_peer_context *obj) { qdf_list_insert_back(obj_list, &obj->peer_node); } static QDF_STATUS wlan_mlo_peerlist_remove_mlpeer( qdf_list_t *obj_list, struct wlan_mlo_peer_context *ml_peer) { qdf_list_node_t *peer_node = NULL; if (!ml_peer) return QDF_STATUS_E_FAILURE; /* get vdev list node element */ peer_node = &ml_peer->peer_node; /* list is empty, return failure */ if (qdf_list_remove_node(obj_list, peer_node) != QDF_STATUS_SUCCESS) return QDF_STATUS_E_FAILURE; return QDF_STATUS_SUCCESS; } QDF_STATUS mlo_dev_mlpeer_attach(struct wlan_mlo_dev_context *ml_dev, struct wlan_mlo_peer_context *ml_peer) { uint8_t hash_index; struct wlan_mlo_peer_list *mlo_peer_list; mlo_peer_list = &ml_dev->mlo_peer_list; ml_peerlist_lock_acquire(mlo_peer_list); if (mlo_get_mlpeer(ml_dev, &ml_peer->peer_mld_addr)) { ml_peerlist_lock_release(mlo_peer_list); mlo_err("MLD ID %d ML Peer exists with mac " QDF_MAC_ADDR_FMT, ml_dev->mld_id, QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); return QDF_STATUS_E_EXISTS; } hash_index = WLAN_PEER_HASH(ml_peer->peer_mld_addr.bytes); wlan_mlo_peerlist_add_tail(&mlo_peer_list->peer_hash[hash_index], ml_peer); ml_peerlist_lock_release(mlo_peer_list); mlo_debug("MLD ID %d ML Peer " QDF_MAC_ADDR_FMT " is attached", ml_dev->mld_id, QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); return QDF_STATUS_SUCCESS; } QDF_STATUS mlo_dev_mlpeer_detach(struct wlan_mlo_dev_context *ml_dev, struct wlan_mlo_peer_context *ml_peer) { uint8_t hash_index; QDF_STATUS status; struct wlan_mlo_peer_list *mlo_peer_list; mlo_peer_list = &ml_dev->mlo_peer_list; ml_peerlist_lock_acquire(mlo_peer_list); hash_index = WLAN_PEER_HASH(ml_peer->peer_mld_addr.bytes); status = wlan_mlo_peerlist_remove_mlpeer( &mlo_peer_list->peer_hash[hash_index], ml_peer); ml_peerlist_lock_release(mlo_peer_list); mlo_debug("MLD ID %d ML Peer " QDF_MAC_ADDR_FMT " is detached", ml_dev->mld_id, QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); return status; } QDF_STATUS mlo_dev_mlpeer_list_init(struct wlan_mlo_dev_context *ml_dev) { struct wlan_mlo_peer_list *mlo_peer_list; uint16_t i; mlo_peer_list = &ml_dev->mlo_peer_list; ml_peerlist_lock_create(mlo_peer_list); for (i = 0; i < WLAN_PEER_HASHSIZE; i++) qdf_list_create(&mlo_peer_list->peer_hash[i], WLAN_UMAC_PSOC_MAX_PEERS + WLAN_MAX_PSOC_TEMP_PEERS); if (ml_dev->ap_ctx) { qdf_spinlock_create(&ml_dev->ap_ctx->assoc_list.list_lock); qdf_list_create(&ml_dev->ap_ctx->assoc_list.peer_list, WLAN_UMAC_PSOC_MAX_PEERS); } return QDF_STATUS_SUCCESS; } QDF_STATUS mlo_dev_mlpeer_list_deinit(struct wlan_mlo_dev_context *ml_dev) { uint16_t i; struct wlan_mlo_peer_list *mlo_peer_list; if (ml_dev->ap_ctx) { qdf_list_destroy(&ml_dev->ap_ctx->assoc_list.peer_list); qdf_spinlock_destroy(&ml_dev->ap_ctx->assoc_list.list_lock); } mlo_peer_list = &ml_dev->mlo_peer_list; for (i = 0; i < WLAN_PEER_HASHSIZE; i++) qdf_list_destroy(&mlo_peer_list->peer_hash[i]); ml_peerlist_lock_destroy(mlo_peer_list); return QDF_STATUS_SUCCESS; }