Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 1 | /* |
Anurag Chouhan | 6d76066 | 2016-02-20 16:05:43 +0530 | [diff] [blame] | 2 | * Copyright (c) 2011, 2014-2016 The Linux Foundation. All rights reserved. |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 3 | * |
| 4 | * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| 5 | * |
| 6 | * |
| 7 | * Permission to use, copy, modify, and/or distribute this software for |
| 8 | * any purpose with or without fee is hereby granted, provided that the |
| 9 | * above copyright notice and this permission notice appear in all |
| 10 | * copies. |
| 11 | * |
| 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| 15 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| 16 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| 17 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| 18 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| 19 | * PERFORMANCE OF THIS SOFTWARE. |
| 20 | */ |
| 21 | |
| 22 | /* |
| 23 | * This file was originally distributed by Qualcomm Atheros, Inc. |
| 24 | * under proprietary terms before Copyright ownership was assigned |
| 25 | * to the Linux Foundation. |
| 26 | */ |
| 27 | |
| 28 | /* standard header files */ |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 29 | #include <qdf_nbuf.h> /* qdf_nbuf_map */ |
Anurag Chouhan | 600c3a0 | 2016-03-01 10:33:54 +0530 | [diff] [blame] | 30 | #include <qdf_mem.h> /* qdf_mem_cmp */ |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 31 | |
| 32 | /* external header files */ |
| 33 | #include <ol_cfg.h> /* wlan_op_mode_ap, etc. */ |
| 34 | #include <ol_htt_rx_api.h> /* htt_rx_msdu_desc_retrieve */ |
| 35 | #include <cds_ieee80211_common.h> /* ieee80211_frame, etc. */ |
| 36 | |
| 37 | /* internal header files */ |
| 38 | #include <ol_txrx_types.h> /* ol_txrx_dev_t, etc. */ |
| 39 | #include <ol_rx_fwd.h> /* our own defs */ |
| 40 | #include <ol_rx.h> /* ol_rx_deliver */ |
| 41 | #include <ol_txrx_internal.h> /* TXRX_ASSERT1 */ |
| 42 | #include <ol_tx.h> |
| 43 | /* |
| 44 | * Porting from Ap11PrepareForwardedPacket. |
| 45 | * This routine is called when a RX data frame from an associated station is |
| 46 | * to be forwarded to another associated station. We will prepare the |
| 47 | * received packet so that it is suitable for transmission again. |
| 48 | * Check that this Packet is suitable for forwarding. If yes, then |
| 49 | * prepare the new 802.11 header. |
| 50 | */ |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 51 | static inline void ol_ap_fwd_check(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu) |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 52 | { |
| 53 | struct ieee80211_frame *mac_header; |
| 54 | unsigned char tmp_addr[IEEE80211_ADDR_LEN]; |
| 55 | unsigned char type; |
| 56 | unsigned char subtype; |
| 57 | unsigned char fromds; |
| 58 | unsigned char tods; |
| 59 | |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 60 | mac_header = (struct ieee80211_frame *)(qdf_nbuf_data(msdu)); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 61 | TXRX_ASSERT1(mac_header); |
| 62 | |
| 63 | type = mac_header->i_fc[0] & IEEE80211_FC0_TYPE_MASK; |
| 64 | subtype = mac_header->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; |
| 65 | tods = mac_header->i_fc[1] & IEEE80211_FC1_DIR_TODS; |
| 66 | fromds = mac_header->i_fc[1] & IEEE80211_FC1_DIR_FROMDS; |
| 67 | |
| 68 | /* |
| 69 | * Make sure no QOS or any other non-data subtype |
| 70 | * Should be a ToDs data frame. |
| 71 | * Make sure that this frame is unicast and not for us. |
| 72 | * These packets should come up through the normal rx path and |
| 73 | * not forwarded. |
| 74 | */ |
| 75 | if (type != IEEE80211_FC0_TYPE_DATA || |
| 76 | subtype != 0x0 || |
| 77 | ((tods != 1) || (fromds != 0)) || |
Anurag Chouhan | 600c3a0 | 2016-03-01 10:33:54 +0530 | [diff] [blame] | 78 | (qdf_mem_cmp |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 79 | (mac_header->i_addr3, vdev->mac_addr.raw, |
Anurag Chouhan | 600c3a0 | 2016-03-01 10:33:54 +0530 | [diff] [blame] | 80 | IEEE80211_ADDR_LEN) != 0)) { |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 81 | #ifdef DEBUG_HOST_RC |
| 82 | TXRX_PRINT(TXRX_PRINT_LEVEL_INFO1, |
| 83 | "Exit: %s | Unnecessary to adjust mac header\n", |
| 84 | __func__); |
| 85 | #endif |
| 86 | } else { |
| 87 | /* Flip the ToDs bit to FromDs */ |
| 88 | mac_header->i_fc[1] &= 0xfe; |
| 89 | mac_header->i_fc[1] |= 0x2; |
| 90 | |
| 91 | /* |
| 92 | * Flip the addresses |
| 93 | * (ToDs, addr1, RA=BSSID) move to (FrDs, addr2, TA=BSSID) |
| 94 | * (ToDs, addr2, SA) move to (FrDs, addr3, SA) |
| 95 | * (ToDs, addr3, DA) move to (FrDs, addr1, DA) |
| 96 | */ |
| 97 | |
| 98 | memcpy(tmp_addr, mac_header->i_addr2, sizeof(tmp_addr)); |
| 99 | |
| 100 | memcpy(mac_header->i_addr2, |
| 101 | mac_header->i_addr1, sizeof(tmp_addr)); |
| 102 | |
| 103 | memcpy(mac_header->i_addr1, |
| 104 | mac_header->i_addr3, sizeof(tmp_addr)); |
| 105 | |
| 106 | memcpy(mac_header->i_addr3, tmp_addr, sizeof(tmp_addr)); |
| 107 | } |
| 108 | } |
| 109 | |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 110 | static inline void ol_rx_fwd_to_tx(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu) |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 111 | { |
| 112 | struct ol_txrx_pdev_t *pdev = vdev->pdev; |
| 113 | |
| 114 | if (pdev->frame_format == wlan_frm_fmt_native_wifi) |
| 115 | ol_ap_fwd_check(vdev, msdu); |
| 116 | |
| 117 | /* |
| 118 | * Map the netbuf, so it's accessible to the DMA that |
| 119 | * sends it to the target. |
| 120 | */ |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 121 | qdf_nbuf_map_single(pdev->osdev, msdu, QDF_DMA_TO_DEVICE); |
| 122 | qdf_nbuf_set_next(msdu, NULL); /* add NULL terminator */ |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 123 | |
| 124 | msdu = OL_TX_LL(vdev, msdu); |
| 125 | |
| 126 | if (msdu) { |
| 127 | /* |
| 128 | * The frame was not accepted by the tx. |
| 129 | * We could store the frame and try again later, |
| 130 | * but the simplest solution is to discard the frames. |
| 131 | */ |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 132 | qdf_nbuf_unmap_single(pdev->osdev, msdu, QDF_DMA_TO_DEVICE); |
| 133 | qdf_nbuf_tx_free(msdu, QDF_NBUF_PKT_ERROR); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 134 | } |
| 135 | } |
| 136 | |
| 137 | void |
| 138 | ol_rx_fwd_check(struct ol_txrx_vdev_t *vdev, |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 139 | struct ol_txrx_peer_t *peer, unsigned tid, qdf_nbuf_t msdu_list) |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 140 | { |
| 141 | struct ol_txrx_pdev_t *pdev = vdev->pdev; |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 142 | qdf_nbuf_t deliver_list_head = NULL; |
| 143 | qdf_nbuf_t deliver_list_tail = NULL; |
| 144 | qdf_nbuf_t msdu; |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 145 | |
| 146 | msdu = msdu_list; |
| 147 | while (msdu) { |
| 148 | struct ol_txrx_vdev_t *tx_vdev; |
| 149 | void *rx_desc; |
| 150 | /* |
| 151 | * Remember the next list elem, because our processing |
| 152 | * may cause the MSDU to get linked into a different list. |
| 153 | */ |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 154 | msdu_list = qdf_nbuf_next(msdu); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 155 | |
| 156 | rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu); |
| 157 | |
| 158 | if (!vdev->disable_intrabss_fwd && |
| 159 | htt_rx_msdu_forward(pdev->htt_pdev, rx_desc)) { |
| 160 | /* |
| 161 | * Use the same vdev that received the frame to |
| 162 | * transmit the frame. |
| 163 | * This is exactly what we want for intra-BSS |
| 164 | * forwarding, like STA-to-STA forwarding and |
| 165 | * multicast echo. |
| 166 | * If this is a intra-BSS forwarding case (which is not |
| 167 | * currently supported), then the tx vdev is different |
| 168 | * from the rx vdev. |
| 169 | * On the LL host the vdevs are not actually used |
| 170 | * for tx, so it would still work to use the rx vdev |
| 171 | * rather than the tx vdev. |
| 172 | * For HL, the tx classification searches for the DA |
| 173 | * within the given vdev, so we would want to get the DA |
| 174 | * peer ID from the target, so we can locate |
| 175 | * the tx vdev. |
| 176 | */ |
| 177 | tx_vdev = vdev; |
| 178 | /* |
| 179 | * Copying TID value of RX packet to forwarded |
| 180 | * packet if the tid is other than non qos tid. |
| 181 | * But for non qos tid fill invalid tid so that |
| 182 | * Fw will take care of filling proper tid. |
| 183 | */ |
| 184 | if (tid != HTT_NON_QOS_TID) { |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 185 | qdf_nbuf_set_tid(msdu, tid); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 186 | } else { |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 187 | qdf_nbuf_set_tid(msdu, |
Anurag Chouhan | c73697b | 2016-02-21 15:05:43 +0530 | [diff] [blame] | 188 | QDF_NBUF_TX_EXT_TID_INVALID); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 189 | } |
| 190 | /* |
| 191 | * This MSDU needs to be forwarded to the tx path. |
| 192 | * Check whether it also needs to be sent to the OS |
| 193 | * shim, in which case we need to make a copy |
| 194 | * (or clone?). |
| 195 | */ |
| 196 | if (htt_rx_msdu_discard(pdev->htt_pdev, rx_desc)) { |
| 197 | htt_rx_msdu_desc_free(pdev->htt_pdev, msdu); |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 198 | qdf_net_buf_debug_release_skb(msdu); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 199 | ol_rx_fwd_to_tx(tx_vdev, msdu); |
| 200 | msdu = NULL; /* already handled this MSDU */ |
| 201 | TXRX_STATS_ADD(pdev, |
| 202 | pub.rx.intra_bss_fwd.packets_fwd, 1); |
| 203 | } else { |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 204 | qdf_nbuf_t copy; |
| 205 | copy = qdf_nbuf_copy(msdu); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 206 | if (copy) |
| 207 | ol_rx_fwd_to_tx(tx_vdev, copy); |
| 208 | TXRX_STATS_ADD(pdev, |
| 209 | pub.rx.intra_bss_fwd.packets_stack_n_fwd, 1); |
| 210 | } |
| 211 | } else { |
| 212 | TXRX_STATS_ADD(pdev, |
| 213 | pub.rx.intra_bss_fwd.packets_stack, 1); |
| 214 | } |
| 215 | if (msdu) { |
| 216 | /* send this frame to the OS */ |
| 217 | OL_TXRX_LIST_APPEND(deliver_list_head, |
| 218 | deliver_list_tail, msdu); |
| 219 | } |
| 220 | msdu = msdu_list; |
| 221 | } |
| 222 | if (deliver_list_head) { |
| 223 | /* add NULL terminator */ |
Nirav Shah | cbc6d72 | 2016-03-01 16:24:53 +0530 | [diff] [blame^] | 224 | qdf_nbuf_set_next(deliver_list_tail, NULL); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 225 | if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev)) { |
| 226 | ol_rx_in_order_deliver(vdev, peer, tid, |
| 227 | deliver_list_head); |
| 228 | } else { |
| 229 | ol_rx_deliver(vdev, peer, tid, deliver_list_head); |
| 230 | } |
| 231 | } |
| 232 | } |