18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause-Clear 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include "core.h" 78c2ecf20Sopenharmony_ci#include "peer.h" 88c2ecf20Sopenharmony_ci#include "debug.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistruct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, 118c2ecf20Sopenharmony_ci const u8 *addr) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci struct ath11k_peer *peer; 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci lockdep_assert_held(&ab->base_lock); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci list_for_each_entry(peer, &ab->peers, list) { 188c2ecf20Sopenharmony_ci if (peer->vdev_id != vdev_id) 198c2ecf20Sopenharmony_ci continue; 208c2ecf20Sopenharmony_ci if (!ether_addr_equal(peer->addr, addr)) 218c2ecf20Sopenharmony_ci continue; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci return peer; 248c2ecf20Sopenharmony_ci } 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci return NULL; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic struct ath11k_peer *ath11k_peer_find_by_pdev_idx(struct ath11k_base *ab, 308c2ecf20Sopenharmony_ci u8 pdev_idx, const u8 *addr) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct ath11k_peer *peer; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci lockdep_assert_held(&ab->base_lock); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci list_for_each_entry(peer, &ab->peers, list) { 378c2ecf20Sopenharmony_ci if (peer->pdev_idx != pdev_idx) 388c2ecf20Sopenharmony_ci continue; 398c2ecf20Sopenharmony_ci if (!ether_addr_equal(peer->addr, addr)) 408c2ecf20Sopenharmony_ci continue; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return peer; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return NULL; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, 498c2ecf20Sopenharmony_ci const u8 *addr) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct ath11k_peer *peer; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci lockdep_assert_held(&ab->base_lock); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci list_for_each_entry(peer, &ab->peers, list) { 568c2ecf20Sopenharmony_ci if (!ether_addr_equal(peer->addr, addr)) 578c2ecf20Sopenharmony_ci continue; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return peer; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return NULL; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, 668c2ecf20Sopenharmony_ci int peer_id) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct ath11k_peer *peer; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci lockdep_assert_held(&ab->base_lock); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci list_for_each_entry(peer, &ab->peers, list) 738c2ecf20Sopenharmony_ci if (peer_id == peer->peer_id) 748c2ecf20Sopenharmony_ci return peer; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return NULL; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistruct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab, 808c2ecf20Sopenharmony_ci int vdev_id) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct ath11k_peer *peer; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci spin_lock_bh(&ab->base_lock); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci list_for_each_entry(peer, &ab->peers, list) { 878c2ecf20Sopenharmony_ci if (vdev_id == peer->vdev_id) { 888c2ecf20Sopenharmony_ci spin_unlock_bh(&ab->base_lock); 898c2ecf20Sopenharmony_ci return peer; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci spin_unlock_bh(&ab->base_lock); 938c2ecf20Sopenharmony_ci return NULL; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_civoid ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct ath11k_peer *peer; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci spin_lock_bh(&ab->base_lock); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci peer = ath11k_peer_find_by_id(ab, peer_id); 1038c2ecf20Sopenharmony_ci if (!peer) { 1048c2ecf20Sopenharmony_ci ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n", 1058c2ecf20Sopenharmony_ci peer_id); 1068c2ecf20Sopenharmony_ci goto exit; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer unmap vdev %d peer %pM id %d\n", 1108c2ecf20Sopenharmony_ci peer->vdev_id, peer->addr, peer_id); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci list_del(&peer->list); 1138c2ecf20Sopenharmony_ci kfree(peer); 1148c2ecf20Sopenharmony_ci wake_up(&ab->peer_mapping_wq); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ciexit: 1178c2ecf20Sopenharmony_ci spin_unlock_bh(&ab->base_lock); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_civoid ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, 1218c2ecf20Sopenharmony_ci u8 *mac_addr, u16 ast_hash) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct ath11k_peer *peer; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci spin_lock_bh(&ab->base_lock); 1268c2ecf20Sopenharmony_ci peer = ath11k_peer_find(ab, vdev_id, mac_addr); 1278c2ecf20Sopenharmony_ci if (!peer) { 1288c2ecf20Sopenharmony_ci peer = kzalloc(sizeof(*peer), GFP_ATOMIC); 1298c2ecf20Sopenharmony_ci if (!peer) 1308c2ecf20Sopenharmony_ci goto exit; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci peer->vdev_id = vdev_id; 1338c2ecf20Sopenharmony_ci peer->peer_id = peer_id; 1348c2ecf20Sopenharmony_ci peer->ast_hash = ast_hash; 1358c2ecf20Sopenharmony_ci ether_addr_copy(peer->addr, mac_addr); 1368c2ecf20Sopenharmony_ci list_add(&peer->list, &ab->peers); 1378c2ecf20Sopenharmony_ci wake_up(&ab->peer_mapping_wq); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer map vdev %d peer %pM id %d\n", 1418c2ecf20Sopenharmony_ci vdev_id, mac_addr, peer_id); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciexit: 1448c2ecf20Sopenharmony_ci spin_unlock_bh(&ab->base_lock); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id, 1488c2ecf20Sopenharmony_ci const u8 *addr, bool expect_mapped) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci ret = wait_event_timeout(ab->peer_mapping_wq, ({ 1538c2ecf20Sopenharmony_ci bool mapped; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci spin_lock_bh(&ab->base_lock); 1568c2ecf20Sopenharmony_ci mapped = !!ath11k_peer_find(ab, vdev_id, addr); 1578c2ecf20Sopenharmony_ci spin_unlock_bh(&ab->base_lock); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci (mapped == expect_mapped || 1608c2ecf20Sopenharmony_ci test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)); 1618c2ecf20Sopenharmony_ci }), 3 * HZ); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (ret <= 0) 1648c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_civoid ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct ath11k_peer *peer, *tmp; 1728c2ecf20Sopenharmony_ci struct ath11k_base *ab = ar->ab; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci spin_lock_bh(&ab->base_lock); 1778c2ecf20Sopenharmony_ci list_for_each_entry_safe(peer, tmp, &ab->peers, list) { 1788c2ecf20Sopenharmony_ci if (peer->vdev_id != vdev_id) 1798c2ecf20Sopenharmony_ci continue; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n", 1828c2ecf20Sopenharmony_ci peer->addr, vdev_id); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci list_del(&peer->list); 1858c2ecf20Sopenharmony_ci kfree(peer); 1868c2ecf20Sopenharmony_ci ar->num_peers--; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci spin_unlock_bh(&ab->base_lock); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, false); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciint ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id, 1988c2ecf20Sopenharmony_ci const u8 *addr) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int ret; 2018c2ecf20Sopenharmony_ci unsigned long time_left; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr); 2048c2ecf20Sopenharmony_ci if (ret) { 2058c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, "failed wait for peer deleted"); 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci time_left = wait_for_completion_timeout(&ar->peer_delete_done, 2108c2ecf20Sopenharmony_ci 3 * HZ); 2118c2ecf20Sopenharmony_ci if (time_left == 0) { 2128c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, "Timeout in receiving peer delete response\n"); 2138c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ciint ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci int ret; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci reinit_completion(&ar->peer_delete_done); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); 2288c2ecf20Sopenharmony_ci if (ret) { 2298c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, 2308c2ecf20Sopenharmony_ci "failed to delete peer vdev_id %d addr %pM ret %d\n", 2318c2ecf20Sopenharmony_ci vdev_id, addr, ret); 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = ath11k_wait_for_peer_delete_done(ar, vdev_id, addr); 2368c2ecf20Sopenharmony_ci if (ret) 2378c2ecf20Sopenharmony_ci return ret; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ar->num_peers--; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int ath11k_wait_for_peer_created(struct ath11k *ar, int vdev_id, const u8 *addr) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, true); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ciint ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, 2508c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, struct peer_create_params *param) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct ath11k_peer *peer; 2538c2ecf20Sopenharmony_ci int ret; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (ar->num_peers > (ar->max_num_peers - 1)) { 2588c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, 2598c2ecf20Sopenharmony_ci "failed to create peer due to insufficient peer entry resource in firmware\n"); 2608c2ecf20Sopenharmony_ci return -ENOBUFS; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci spin_lock_bh(&ar->ab->base_lock); 2648c2ecf20Sopenharmony_ci peer = ath11k_peer_find_by_pdev_idx(ar->ab, ar->pdev_idx, param->peer_addr); 2658c2ecf20Sopenharmony_ci if (peer) { 2668c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->ab->base_lock); 2678c2ecf20Sopenharmony_ci return -EINVAL; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->ab->base_lock); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ret = ath11k_wmi_send_peer_create_cmd(ar, param); 2728c2ecf20Sopenharmony_ci if (ret) { 2738c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, 2748c2ecf20Sopenharmony_ci "failed to send peer create vdev_id %d ret %d\n", 2758c2ecf20Sopenharmony_ci param->vdev_id, ret); 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci ret = ath11k_wait_for_peer_created(ar, param->vdev_id, 2808c2ecf20Sopenharmony_ci param->peer_addr); 2818c2ecf20Sopenharmony_ci if (ret) 2828c2ecf20Sopenharmony_ci return ret; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci spin_lock_bh(&ar->ab->base_lock); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr); 2878c2ecf20Sopenharmony_ci if (!peer) { 2888c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->ab->base_lock); 2898c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", 2908c2ecf20Sopenharmony_ci param->peer_addr, param->vdev_id); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci reinit_completion(&ar->peer_delete_done); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, 2958c2ecf20Sopenharmony_ci param->vdev_id); 2968c2ecf20Sopenharmony_ci if (ret) { 2978c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", 2988c2ecf20Sopenharmony_ci param->vdev_id, param->peer_addr); 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, 3038c2ecf20Sopenharmony_ci param->peer_addr); 3048c2ecf20Sopenharmony_ci if (ret) 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return -ENOENT; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci peer->pdev_idx = ar->pdev_idx; 3118c2ecf20Sopenharmony_ci peer->sta = sta; 3128c2ecf20Sopenharmony_ci arvif->ast_hash = peer->ast_hash; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci peer->sec_type = HAL_ENCRYPT_TYPE_OPEN; 3158c2ecf20Sopenharmony_ci peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ar->num_peers++; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->ab->base_lock); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 323