blob: 7653928fb231cbd7682ebd5a81c70719382b3a99 [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 <qdf_nbuf.h> /* qdf_nbuf_map */
#include <qdf_mem.h> /* qdf_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 <cds_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 */
#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, qdf_nbuf_t msdu)
{
struct ieee80211_frame *mac_header;
unsigned char tmp_addr[IEEE80211_ADDR_LEN];
unsigned char type;
unsigned char subtype;
unsigned char fromds;
unsigned char tods;
mac_header = (struct ieee80211_frame *)(qdf_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)) ||
(qdf_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, qdf_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.
*/
qdf_nbuf_map_single(pdev->osdev, msdu, QDF_DMA_TO_DEVICE);
qdf_nbuf_set_next(msdu, NULL); /* add NULL terminator */
msdu = OL_TX_LL(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.
*/
qdf_nbuf_unmap_single(pdev->osdev, msdu, QDF_DMA_TO_DEVICE);
qdf_nbuf_tx_free(msdu, QDF_NBUF_PKT_ERROR);
}
}
void
ol_rx_fwd_check(struct ol_txrx_vdev_t *vdev,
struct ol_txrx_peer_t *peer, unsigned tid, qdf_nbuf_t msdu_list)
{
struct ol_txrx_pdev_t *pdev = vdev->pdev;
qdf_nbuf_t deliver_list_head = NULL;
qdf_nbuf_t deliver_list_tail = NULL;
qdf_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 = qdf_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)) {
/*
* 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) {
qdf_nbuf_set_tid(msdu, tid);
} else {
qdf_nbuf_set_tid(msdu,
QDF_NBUF_TX_EXT_TID_INVALID);
}
/*
* 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 (htt_rx_msdu_discard(pdev->htt_pdev, rx_desc)) {
htt_rx_msdu_desc_free(pdev->htt_pdev, msdu);
qdf_net_buf_debug_release_skb(msdu);
ol_rx_fwd_to_tx(tx_vdev, msdu);
msdu = NULL; /* already handled this MSDU */
TXRX_STATS_ADD(pdev,
pub.rx.intra_bss_fwd.packets_fwd, 1);
} else {
qdf_nbuf_t copy;
copy = qdf_nbuf_copy(msdu);
if (copy)
ol_rx_fwd_to_tx(tx_vdev, copy);
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) {
/* add NULL terminator */
qdf_nbuf_set_next(deliver_list_tail, NULL);
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);
}
}
}