1 /* 2 * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for 6 * any purpose with or without fee is hereby granted, provided that the 7 * above copyright notice and this permission notice appear in all 8 * copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #ifndef _DP_TXRX_WDS_H_ 21 #define _DP_TXRX_WDS_H_ 22 23 #ifdef WIFI_MONITOR_SUPPORT 24 #include "dp_htt.h" 25 #include "dp_mon.h" 26 #endif 27 28 /* host managed flag */ 29 #define DP_AST_FLAGS_HM 0x0020 30 31 /* WDS AST entry aging timer value */ 32 #define DP_WDS_AST_AGING_TIMER_DEFAULT_MS 120000 33 #define DP_WDS_AST_AGING_TIMER_CNT \ 34 ((DP_WDS_AST_AGING_TIMER_DEFAULT_MS / DP_AST_AGING_TIMER_DEFAULT_MS) - 1) 35 void dp_soc_wds_attach(struct dp_soc *soc); 36 void dp_soc_wds_detach(struct dp_soc *soc); 37 #ifdef QCA_PEER_MULTIQ_SUPPORT 38 int dp_peer_find_ast_index_by_flowq_id(struct cdp_soc_t *soc, 39 uint16_t vdev_id, uint8_t *peer_mac_addr, 40 uint8_t flow_id, uint8_t tid); 41 #endif 42 void 43 dp_rx_da_learn(struct dp_soc *soc, 44 uint8_t *rx_tlv_hdr, 45 struct dp_txrx_peer *ta_peer, 46 qdf_nbuf_t nbuf); 47 48 void dp_tx_mec_handler(struct dp_vdev *vdev, uint8_t *status); 49 #ifdef FEATURE_WDS 50 #ifdef FEATURE_MCL_REPEATER 51 static inline bool dp_tx_da_search_override(struct dp_vdev *vdev) 52 { 53 if (vdev->mec_enabled) 54 return true; 55 56 return false; 57 } 58 #else 59 static inline bool dp_tx_da_search_override(struct dp_vdev *vdev) 60 { 61 struct dp_soc *soc = vdev->pdev->soc; 62 63 /* 64 * If AST index override support is available (HKv2 etc), 65 * DA search flag be enabled always 66 * 67 * If AST index override support is not available (HKv1), 68 * DA search flag should be used for all modes except QWRAP 69 */ 70 if (soc->ast_override_support || !vdev->proxysta_vdev) 71 return true; 72 73 return false; 74 } 75 #endif /* FEATURE_MCL_REPEATER */ 76 #endif /* FEATURE_WDS */ 77 #ifdef WDS_VENDOR_EXTENSION 78 QDF_STATUS 79 dp_txrx_peer_wds_tx_policy_update(struct cdp_soc_t *cdp_soc, uint8_t vdev_id, 80 uint8_t *peer_mac, int wds_tx_ucast, 81 int wds_tx_mcast); 82 QDF_STATUS 83 dp_txrx_set_wds_rx_policy(struct cdp_soc_t *cdp_soc, uint8_t vdev_id, 84 u_int32_t val); 85 #endif 86 87 /** 88 * dp_hmwds_ast_add_notify() - schedules hmwds ast add status work 89 * @peer: DP peer 90 * @mac_addr: ast mac addr 91 * @type: ast type 92 * @err: QDF_STATUS error code 93 * @is_peer_map: notify is from peer map 94 * 95 * Return: void 96 */ 97 void dp_hmwds_ast_add_notify(struct dp_peer *peer, 98 uint8_t *mac_addr, 99 enum cdp_txrx_ast_entry_type type, 100 QDF_STATUS err, 101 bool is_peer_map); 102 103 #ifdef QCA_SUPPORT_WDS_EXTENDED 104 /** 105 * dp_wds_ext_peer_learn() - function to send event to control 106 * path on receiving 1st 4-address frame from backhaul. 107 * @soc: DP soc 108 * @ta_peer: WDS repeater peer 109 * 110 * Return: void 111 */ 112 static inline void dp_wds_ext_peer_learn(struct dp_soc *soc, 113 struct dp_peer *ta_peer) 114 { 115 uint8_t wds_ext_src_mac[QDF_MAC_ADDR_SIZE]; 116 117 if (ta_peer->vdev->wds_ext_enabled && 118 !qdf_atomic_test_and_set_bit(WDS_EXT_PEER_INIT_BIT, 119 &ta_peer->txrx_peer->wds_ext.init)) { 120 qdf_mem_copy(wds_ext_src_mac, &ta_peer->mac_addr.raw[0], 121 QDF_MAC_ADDR_SIZE); 122 soc->cdp_soc.ol_ops->rx_wds_ext_peer_learn( 123 soc->ctrl_psoc, 124 ta_peer->peer_id, 125 ta_peer->vdev->vdev_id, 126 wds_ext_src_mac); 127 } 128 } 129 #else 130 static inline void dp_wds_ext_peer_learn(struct dp_soc *soc, 131 struct dp_peer *ta_peer) 132 { 133 } 134 #endif 135 136 /** 137 * dp_rx_wds_add_or_update_ast() - Add or update the ast entry. 138 * 139 * @soc: core txrx main context 140 * @ta_txrx_peer: WDS repeater txrx peer 141 * @mac_addr: mac address of the peer 142 * @is_ad4_valid: 4-address valid flag 143 * @is_sa_valid: source address valid flag 144 * @is_chfrag_start: frag start flag 145 * @sa_idx: source-address index for peer 146 * @sa_sw_peer_id: software source-address peer-id 147 * 148 * Return: void: 149 */ 150 static inline void 151 dp_rx_wds_add_or_update_ast(struct dp_soc *soc, 152 struct dp_txrx_peer *ta_peer, 153 qdf_nbuf_t nbuf, uint8_t is_ad4_valid, 154 uint8_t is_sa_valid, uint8_t is_chfrag_start, 155 uint16_t sa_idx, uint16_t sa_sw_peer_id) 156 { 157 struct dp_peer *sa_peer; 158 struct dp_ast_entry *ast; 159 uint32_t flags = DP_AST_FLAGS_HM; 160 uint32_t ret = 0; 161 struct dp_pdev *pdev = ta_peer->vdev->pdev; 162 uint8_t wds_src_mac[QDF_MAC_ADDR_SIZE]; 163 struct dp_peer *ta_base_peer; 164 165 166 if (!(is_chfrag_start && is_ad4_valid)) 167 return; 168 169 if (qdf_unlikely(!is_sa_valid)) { 170 qdf_mem_copy(wds_src_mac, 171 (qdf_nbuf_data(nbuf) + QDF_MAC_ADDR_SIZE), 172 QDF_MAC_ADDR_SIZE); 173 174 ta_base_peer = dp_peer_get_ref_by_id(soc, ta_peer->peer_id, 175 DP_MOD_ID_RX); 176 if (ta_base_peer) { 177 if (ta_peer->vdev->opmode == wlan_op_mode_ap) 178 dp_wds_ext_peer_learn(soc, ta_base_peer); 179 180 ret = dp_peer_add_ast(soc, ta_base_peer, wds_src_mac, 181 CDP_TXRX_AST_TYPE_WDS, flags); 182 183 dp_peer_unref_delete(ta_base_peer, DP_MOD_ID_RX); 184 } 185 return; 186 } 187 188 qdf_spin_lock_bh(&soc->ast_lock); 189 ast = soc->ast_table[sa_idx]; 190 191 if (!ast) { 192 qdf_spin_unlock_bh(&soc->ast_lock); 193 /* 194 * In HKv1, it is possible that HW retains the AST entry in 195 * GSE cache on 1 radio , even after the AST entry is deleted 196 * (on another radio). 197 * 198 * Due to this, host might still get sa_is_valid indications 199 * for frames with SA not really present in AST table. 200 * 201 * So we go ahead and send an add_ast command to FW in such 202 * cases where sa is reported still as valid, so that FW will 203 * invalidate this GSE cache entry and new AST entry gets 204 * cached. 205 */ 206 if (!soc->ast_override_support) { 207 qdf_mem_copy(wds_src_mac, 208 (qdf_nbuf_data(nbuf) + QDF_MAC_ADDR_SIZE), 209 QDF_MAC_ADDR_SIZE); 210 211 ta_base_peer = dp_peer_get_ref_by_id(soc, 212 ta_peer->peer_id, 213 DP_MOD_ID_RX); 214 215 if (ta_base_peer) { 216 ret = dp_peer_add_ast(soc, ta_base_peer, 217 wds_src_mac, 218 CDP_TXRX_AST_TYPE_WDS, 219 flags); 220 221 dp_peer_unref_delete(ta_base_peer, 222 DP_MOD_ID_RX); 223 } 224 return; 225 } else { 226 /* In HKv2 smart monitor case, when NAC client is 227 * added first and this client roams within BSS to 228 * connect to RE, since we have an AST entry for 229 * NAC we get sa_is_valid bit set. So we check if 230 * smart monitor is enabled and send add_ast command 231 * to FW. 232 */ 233 ta_base_peer = dp_peer_get_ref_by_id(soc, 234 ta_peer->peer_id, 235 DP_MOD_ID_RX); 236 if (ta_base_peer) { 237 dp_monitor_neighbour_peer_add_ast(pdev, 238 ta_base_peer, 239 wds_src_mac, 240 nbuf, 241 flags); 242 dp_peer_unref_delete(ta_base_peer, 243 DP_MOD_ID_RX); 244 } 245 return; 246 } 247 } 248 249 /* 250 * Ensure we are updating the right AST entry by 251 * validating ast_idx. 252 * There is a possibility we might arrive here without 253 * AST MAP event , so this check is mandatory 254 */ 255 if (ast->is_mapped && (ast->ast_idx == sa_idx)) 256 ast->is_active = TRUE; 257 258 if (ast->peer_id != ta_peer->peer_id) { 259 if ((ast->type == CDP_TXRX_AST_TYPE_WDS_HM) || 260 (ast->type == CDP_TXRX_AST_TYPE_WDS_HM_SEC)) { 261 qdf_spin_unlock_bh(&soc->ast_lock); 262 return; 263 } 264 265 if ((ast->type != CDP_TXRX_AST_TYPE_STATIC) && 266 (ast->type != CDP_TXRX_AST_TYPE_SELF) && 267 (ast->type != CDP_TXRX_AST_TYPE_STA_BSS)) { 268 if (ast->pdev_id != ta_peer->vdev->pdev->pdev_id) { 269 /* This case is when a STA roams from one 270 * repeater to another repeater, but these 271 * repeaters are connected to root AP on 272 * different radios. 273 * Ex: rptr1 connected to ROOT AP over 5G 274 * and rptr2 connected to ROOT AP over 2G 275 * radio 276 */ 277 dp_peer_del_ast(soc, ast); 278 } else { 279 /* this case is when a STA roams from one 280 * reapter to another repeater, but inside 281 * same radio. 282 */ 283 /* For HKv2 do not update the AST entry if 284 * new ta_peer is on STA vap as SRC port 285 * learning is disable on STA vap 286 */ 287 if (soc->ast_override_support && 288 (ta_peer->vdev->opmode == wlan_op_mode_sta)) { 289 dp_peer_del_ast(soc, ast); 290 } else { 291 ta_base_peer = 292 dp_peer_get_ref_by_id(soc, 293 ta_peer->peer_id, 294 DP_MOD_ID_RX); 295 if (ta_base_peer) { 296 dp_wds_ext_peer_learn(soc, 297 ta_base_peer); 298 dp_peer_update_ast(soc, 299 ta_base_peer, 300 ast, flags); 301 302 dp_peer_unref_delete(ta_base_peer, 303 DP_MOD_ID_RX); 304 } 305 } 306 qdf_spin_unlock_bh(&soc->ast_lock); 307 return; 308 } 309 } 310 /* 311 * Do not kickout STA if it belongs to a different radio. 312 * For DBDC repeater, it is possible to arrive here 313 * for multicast loopback frames originated from connected 314 * clients and looped back (intrabss) by Root AP 315 */ 316 if (ast->pdev_id != ta_peer->vdev->pdev->pdev_id) { 317 qdf_spin_unlock_bh(&soc->ast_lock); 318 return; 319 } 320 321 qdf_spin_unlock_bh(&soc->ast_lock); 322 /* 323 * Kickout, when direct associated peer(SA) roams 324 * to another AP and reachable via TA peer 325 */ 326 sa_peer = dp_peer_get_ref_by_id(soc, ast->peer_id, 327 DP_MOD_ID_RX); 328 if (!sa_peer) 329 return; 330 331 if ((sa_peer->vdev->opmode == wlan_op_mode_ap) && 332 !sa_peer->delete_in_progress) { 333 qdf_mem_copy(wds_src_mac, 334 (qdf_nbuf_data(nbuf) + QDF_MAC_ADDR_SIZE), 335 QDF_MAC_ADDR_SIZE); 336 sa_peer->delete_in_progress = true; 337 if (soc->cdp_soc.ol_ops->peer_sta_kickout) { 338 soc->cdp_soc.ol_ops->peer_sta_kickout( 339 soc->ctrl_psoc, 340 sa_peer->vdev->pdev->pdev_id, 341 wds_src_mac); 342 } 343 } 344 dp_peer_unref_delete(sa_peer, DP_MOD_ID_RX); 345 return; 346 } 347 348 qdf_spin_unlock_bh(&soc->ast_lock); 349 } 350 351 /** 352 * dp_rx_wds_srcport_learn() - Add or update the STA PEER which 353 * is behind the WDS repeater. 354 * 355 * @soc: core txrx main context 356 * @rx_tlv_hdr: base address of RX TLV header 357 * @ta_peer: WDS repeater peer 358 * @nbuf: rx pkt 359 * 360 * Return: void: 361 */ 362 static inline void 363 dp_rx_wds_srcport_learn(struct dp_soc *soc, 364 uint8_t *rx_tlv_hdr, 365 struct dp_txrx_peer *ta_peer, 366 qdf_nbuf_t nbuf, 367 struct hal_rx_msdu_metadata msdu_end_info) 368 { 369 uint8_t sa_is_valid = qdf_nbuf_is_sa_valid(nbuf); 370 uint8_t is_chfrag_start = 0; 371 uint8_t is_ad4_valid = 0; 372 373 if (qdf_unlikely(!ta_peer)) 374 return; 375 376 is_chfrag_start = qdf_nbuf_is_rx_chfrag_start(nbuf); 377 if (is_chfrag_start) 378 is_ad4_valid = hal_rx_get_mpdu_mac_ad4_valid(soc->hal_soc, rx_tlv_hdr); 379 380 381 /* 382 * Get the AST entry from HW SA index and mark it as active 383 */ 384 dp_rx_wds_add_or_update_ast(soc, ta_peer, nbuf, is_ad4_valid, 385 sa_is_valid, is_chfrag_start, 386 msdu_end_info.sa_idx, msdu_end_info.sa_sw_peer_id); 387 } 388 389 #ifdef IPA_WDS_EASYMESH_FEATURE 390 /** 391 * dp_rx_ipa_wds_srcport_learn() - Add or update the STA PEER which 392 * is behind the WDS repeater. 393 * 394 * @soc: core txrx main context 395 * @ta_peer: WDS repeater peer 396 * @nbuf: rx pkt 397 * @msdu_end_info: msdu end info 398 * @ad4_valid: address4 valid bit 399 * @chfrag_start: Msdu start bit 400 * 401 * Return: void 402 */ 403 static inline void 404 dp_rx_ipa_wds_srcport_learn(struct dp_soc *soc, 405 struct dp_peer *ta_peer, qdf_nbuf_t nbuf, 406 struct hal_rx_msdu_metadata msdu_end_info, 407 bool ad4_valid, bool chfrag_start) 408 { 409 uint8_t sa_is_valid = qdf_nbuf_is_sa_valid(nbuf); 410 uint8_t is_chfrag_start = (uint8_t)chfrag_start; 411 uint8_t is_ad4_valid = (uint8_t)ad4_valid; 412 struct dp_txrx_peer *peer = (struct dp_txrx_peer *)ta_peer; 413 414 if (qdf_unlikely(!ta_peer)) 415 return; 416 417 /* 418 * Get the AST entry from HW SA index and mark it as active 419 */ 420 dp_rx_wds_add_or_update_ast(soc, peer, nbuf, is_ad4_valid, 421 sa_is_valid, is_chfrag_start, 422 msdu_end_info.sa_idx, 423 msdu_end_info.sa_sw_peer_id); 424 } 425 #endif 426 427 /* 428 * dp_rx_ast_set_active() - set the active flag of the astentry 429 * corresponding to a hw index. 430 * @soc: core txrx main context 431 * @sa_idx: hw idx 432 * @is_active: active flag 433 * 434 */ 435 static inline QDF_STATUS dp_rx_ast_set_active(struct dp_soc *soc, 436 uint16_t sa_idx, bool is_active) 437 { 438 struct dp_ast_entry *ast; 439 440 qdf_spin_lock_bh(&soc->ast_lock); 441 ast = soc->ast_table[sa_idx]; 442 443 if (!ast) { 444 qdf_spin_unlock_bh(&soc->ast_lock); 445 return QDF_STATUS_E_NULL_VALUE; 446 } 447 448 if (!ast->is_mapped) { 449 qdf_spin_unlock_bh(&soc->ast_lock); 450 return QDF_STATUS_E_INVAL; 451 } 452 453 /* 454 * Ensure we are updating the right AST entry by 455 * validating ast_idx. 456 * There is a possibility we might arrive here without 457 * AST MAP event , so this check is mandatory 458 */ 459 if (ast->ast_idx == sa_idx) { 460 ast->is_active = is_active; 461 qdf_spin_unlock_bh(&soc->ast_lock); 462 return QDF_STATUS_SUCCESS; 463 } 464 465 qdf_spin_unlock_bh(&soc->ast_lock); 466 return QDF_STATUS_E_FAILURE; 467 } 468 #endif /* DP_TXRX_WDS*/ 469