blob: 70555dc5692ebf8adb77e0ca8edf1978a03e2732 [file] [log] [blame]
/*
* 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;
}