| /* |
| * Copyright (c) 2011, 2014, 2016 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * 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. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| /* standard header files */ |
| #include <adf_nbuf.h> /* adf_nbuf_map */ |
| #include <adf_os_mem.h> /* adf_os_mem_cmp */ |
| |
| /* external header files */ |
| #include <ol_cfg.h> /* wlan_op_mode_ap, etc. */ |
| #include <ol_htt_rx_api.h> /* htt_rx_msdu_desc_retrieve */ |
| #include <ieee80211_common.h> /* ieee80211_frame, etc. */ |
| |
| /* internal header files */ |
| #include <ol_txrx_types.h> /* ol_txrx_dev_t, etc. */ |
| #include <ol_rx_fwd.h> /* our own defs */ |
| #include <ol_rx.h> /* ol_rx_deliver */ |
| #include <ol_txrx_internal.h> /* TXRX_ASSERT1 */ |
| #ifdef QCA_ARP_SPOOFING_WAR |
| #include <ol_if_athvar.h> |
| #endif |
| #include "ol_tx.h" |
| |
| |
| /* |
| * Porting from Ap11PrepareForwardedPacket. |
| * This routine is called when a RX data frame from an associated station is |
| * to be forwarded to another associated station. We will prepare the |
| * received packet so that it is suitable for transmission again. |
| * Check that this Packet is suitable for forwarding. If yes, then |
| * prepare the new 802.11 header. |
| */ |
| static inline |
| void |
| ol_ap_fwd_check(struct ol_txrx_vdev_t *vdev, adf_nbuf_t msdu) |
| { |
| struct ieee80211_frame *mac_header; |
| unsigned char tmp_addr[6]; |
| unsigned char type; |
| unsigned char subtype; |
| unsigned char fromds; |
| unsigned char tods; |
| |
| mac_header = (struct ieee80211_frame *) (adf_nbuf_data(msdu)); |
| TXRX_ASSERT1(mac_header); |
| |
| type = mac_header->i_fc[0] & IEEE80211_FC0_TYPE_MASK; |
| subtype = mac_header->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; |
| tods = mac_header->i_fc[1] & IEEE80211_FC1_DIR_TODS; |
| fromds = mac_header->i_fc[1] & IEEE80211_FC1_DIR_FROMDS; |
| |
| /* |
| * Make sure no QOS or any other non-data subtype |
| * Should be a ToDs data frame. |
| * Make sure that this frame is unicast and not for us. |
| * These packets should come up through the normal rx path and not forwarded. |
| */ |
| if (type != IEEE80211_FC0_TYPE_DATA || |
| subtype != 0x0 || |
| ((tods != 1) || (fromds != 0)) || |
| (adf_os_mem_cmp( |
| mac_header->i_addr3, vdev->mac_addr.raw, IEEE80211_ADDR_LEN) == 0)) |
| { |
| #ifdef DEBUG_HOST_RC |
| TXRX_PRINT(TXRX_PRINT_LEVEL_INFO1, "Exit: %s | Unnecessary to adjust mac header\n", __func__); |
| #endif |
| } |
| else |
| { |
| // Flip the ToDs bit to FromDs |
| mac_header->i_fc[1] &= 0xfe; |
| mac_header->i_fc[1] |= 0x2; |
| |
| /* |
| * Flip the addresses |
| * (ToDs, addr1, RA=BSSID) move to (FrDs, addr2, TA=BSSID) |
| * (ToDs, addr2, SA) move to (FrDs, addr3, SA) |
| * (ToDs, addr3, DA) move to (FrDs, addr1, DA) |
| */ |
| |
| memcpy(tmp_addr, |
| mac_header->i_addr2, |
| sizeof (tmp_addr)); |
| |
| memcpy(mac_header->i_addr2, |
| mac_header->i_addr1, |
| sizeof (tmp_addr)); |
| |
| memcpy(mac_header->i_addr1, |
| mac_header->i_addr3, |
| sizeof (tmp_addr)); |
| |
| memcpy(mac_header->i_addr3, |
| tmp_addr, |
| sizeof (tmp_addr)); |
| } |
| } |
| |
| static inline |
| void |
| ol_rx_fwd_to_tx(struct ol_txrx_vdev_t *vdev, adf_nbuf_t msdu) |
| { |
| struct ol_txrx_pdev_t *pdev = vdev->pdev; |
| |
| if (pdev->frame_format == wlan_frm_fmt_native_wifi) |
| { |
| ol_ap_fwd_check(vdev, msdu); |
| } |
| /* |
| * Map the netbuf, so it's accessible to the DMA that |
| * sends it to the target. |
| */ |
| adf_nbuf_set_next(msdu, NULL); /* add NULL terminator */ |
| |
| /* for HL, point to payload before send to tx again.*/ |
| if (pdev->cfg.is_high_latency) { |
| void *rx_desc; |
| rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu); |
| |
| adf_nbuf_pull_head(msdu, |
| htt_rx_msdu_rx_desc_size_hl(pdev->htt_pdev, |
| rx_desc)); |
| adf_nbuf_set_fwd_flag(msdu, ADF_NBUF_FWD_FLAG); |
| } |
| |
| msdu = vdev->tx(vdev, msdu); |
| |
| if (msdu) { |
| /* |
| * The frame was not accepted by the tx. |
| * We could store the frame and try again later, |
| * but the simplest solution is to discard the frames. |
| */ |
| adf_nbuf_tx_free(msdu, ADF_NBUF_PKT_ERROR); |
| } |
| } |
| |
| void |
| ol_rx_fwd_check( |
| struct ol_txrx_vdev_t *vdev, |
| struct ol_txrx_peer_t *peer, |
| unsigned tid, |
| adf_nbuf_t msdu_list) |
| { |
| struct ol_txrx_pdev_t *pdev = vdev->pdev; |
| adf_nbuf_t deliver_list_head = NULL; |
| adf_nbuf_t deliver_list_tail = NULL; |
| adf_nbuf_t msdu; |
| |
| msdu = msdu_list; |
| while (msdu) { |
| struct ol_txrx_vdev_t *tx_vdev; |
| void *rx_desc; |
| /* |
| * Remember the next list elem, because our processing |
| * may cause the MSDU to get linked into a different list. |
| */ |
| msdu_list = adf_nbuf_next(msdu); |
| |
| rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu); |
| |
| if (!vdev->disable_intrabss_fwd && |
| htt_rx_msdu_forward(pdev->htt_pdev, rx_desc)) { |
| #ifdef QCA_ARP_SPOOFING_WAR |
| void *filter_cb; |
| #endif |
| int do_not_fwd = 0; |
| /* |
| * Use the same vdev that received the frame to |
| * transmit the frame. |
| * This is exactly what we want for intra-BSS forwarding, |
| * like STA-to-STA forwarding and multicast echo. |
| * If this is a intra-BSS forwarding case (which is not |
| * currently supported), then the tx vdev is different |
| * from the rx vdev. |
| * On the LL host the vdevs are not actually used for tx, |
| * so it would still work to use the rx vdev rather than |
| * the tx vdev. |
| * For HL, the tx classification searches for the DA within |
| * the given vdev, so we would want to get the DA peer ID |
| * from the target, so we can locate the tx vdev. |
| */ |
| tx_vdev = vdev; |
| /* |
| * Copying TID value of RX packet to forwarded |
| * packet if the tid is other than non qos tid. |
| * But for non qos tid fill invalid tid so that |
| * Fw will take care of filling proper tid. |
| */ |
| if (tid != HTT_NON_QOS_TID) { |
| adf_nbuf_set_tid(msdu, tid); |
| } else { |
| adf_nbuf_set_tid(msdu, ADF_NBUF_TX_EXT_TID_INVALID); |
| } |
| |
| #ifdef QCA_ARP_SPOOFING_WAR |
| filter_cb = (void *)NBUF_CB_PTR(msdu); |
| if (filter_cb) { |
| do_not_fwd = (*(hdd_filter_cb_t)filter_cb)(vdev->vdev_id, msdu, |
| RX_INTRA_BSS_FWD); |
| } |
| #endif |
| /* |
| * This MSDU needs to be forwarded to the tx path. |
| * Check whether it also needs to be sent to the OS shim, |
| * in which case we need to make a copy (or clone?). |
| */ |
| if (!do_not_fwd) { |
| if (htt_rx_msdu_discard(pdev->htt_pdev, rx_desc)) { |
| htt_rx_msdu_desc_free(pdev->htt_pdev, msdu); |
| adf_net_buf_debug_release_skb(msdu); |
| ol_rx_fwd_to_tx(tx_vdev, msdu); |
| msdu = NULL; /* already handled this MSDU */ |
| tx_vdev->fwd_tx_packets++; |
| vdev->fwd_rx_packets++; |
| TXRX_STATS_ADD(pdev, pub.rx.intra_bss_fwd.packets_fwd, |
| 1); |
| } else { |
| adf_nbuf_t copy; |
| copy = adf_nbuf_copy(msdu); |
| if (copy) { |
| ol_rx_fwd_to_tx(tx_vdev, copy); |
| tx_vdev->fwd_tx_packets++; |
| } |
| TXRX_STATS_ADD(pdev, |
| pub.rx.intra_bss_fwd.packets_stack_n_fwd, 1); |
| } |
| } |
| } else { |
| TXRX_STATS_ADD(pdev, pub.rx.intra_bss_fwd.packets_stack, 1); |
| } |
| if (msdu) { |
| /* send this frame to the OS */ |
| OL_TXRX_LIST_APPEND(deliver_list_head, deliver_list_tail, msdu); |
| } |
| msdu = msdu_list; |
| } |
| if (deliver_list_head) { |
| adf_nbuf_set_next(deliver_list_tail, NULL); /* add NULL terminator */ |
| if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev)) { |
| ol_rx_in_order_deliver(vdev, peer, tid, deliver_list_head); |
| } else { |
| ol_rx_deliver(vdev, peer, tid, deliver_list_head); |
| } |
| } |
| } |
| |
| /* |
| * ol_get_intra_bss_fwd_pkts_count() - to get the total tx and rx packets |
| * that has been forwarded from txrx layer without going to upper layers. |
| * |
| * @vdev_id: vdev id |
| * @fwd_tx_packets: pointer to forwarded tx packets count parameter |
| * @fwd_rx_packets: pointer to forwarded rx packets count parameter |
| * |
| * Return: status -> A_OK - success, A_ERROR - failure |
| * |
| */ |
| A_STATUS ol_get_intra_bss_fwd_pkts_count(uint8_t vdev_id, |
| unsigned long *fwd_tx_packets, unsigned long *fwd_rx_packets) |
| { |
| struct ol_txrx_vdev_t *vdev = NULL; |
| |
| vdev = (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); |
| if (!vdev) |
| return A_ERROR; |
| |
| *fwd_tx_packets = vdev->fwd_tx_packets; |
| *fwd_rx_packets = vdev->fwd_rx_packets; |
| return A_OK; |
| } |
| |