1 /* 2 * Copyright (c) 2011, 2014-2017 The Linux Foundation. All rights reserved. 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 */ 29 #include <qdf_nbuf.h> /* qdf_nbuf_map */ 30 #include <qdf_mem.h> /* qdf_mem_cmp */ 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_rx_fwd.h> /* our own defs */ 39 #include <ol_rx.h> /* ol_rx_deliver */ 40 #include <ol_txrx_internal.h> /* TXRX_ASSERT1 */ 41 #include <ol_tx.h> 42 #include <ol_txrx.h> 43 44 /* 45 * Porting from Ap11PrepareForwardedPacket. 46 * This routine is called when a RX data frame from an associated station is 47 * to be forwarded to another associated station. We will prepare the 48 * received packet so that it is suitable for transmission again. 49 * Check that this Packet is suitable for forwarding. If yes, then 50 * prepare the new 802.11 header. 51 */ 52 static inline void ol_ap_fwd_check(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu) 53 { 54 struct ieee80211_frame *mac_header; 55 unsigned char tmp_addr[IEEE80211_ADDR_LEN]; 56 unsigned char type; 57 unsigned char subtype; 58 unsigned char fromds; 59 unsigned char tods; 60 61 mac_header = (struct ieee80211_frame *)(qdf_nbuf_data(msdu)); 62 TXRX_ASSERT1(mac_header); 63 64 type = mac_header->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 65 subtype = mac_header->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 66 tods = mac_header->i_fc[1] & IEEE80211_FC1_DIR_TODS; 67 fromds = mac_header->i_fc[1] & IEEE80211_FC1_DIR_FROMDS; 68 69 /* 70 * Make sure no QOS or any other non-data subtype 71 * Should be a ToDs data frame. 72 * Make sure that this frame is unicast and not for us. 73 * These packets should come up through the normal rx path and 74 * not forwarded. 75 */ 76 if (type != IEEE80211_FC0_TYPE_DATA || 77 subtype != 0x0 || 78 ((tods != 1) || (fromds != 0)) || 79 qdf_mem_cmp 80 (mac_header->i_addr3, vdev->mac_addr.raw, 81 IEEE80211_ADDR_LEN)) { 82 ol_txrx_dbg("Exit: %s | Unnecessary to adjust mac header\n", 83 __func__); 84 } else { 85 /* Flip the ToDs bit to FromDs */ 86 mac_header->i_fc[1] &= 0xfe; 87 mac_header->i_fc[1] |= 0x2; 88 89 /* 90 * Flip the addresses 91 * (ToDs, addr1, RA=BSSID) move to (FrDs, addr2, TA=BSSID) 92 * (ToDs, addr2, SA) move to (FrDs, addr3, SA) 93 * (ToDs, addr3, DA) move to (FrDs, addr1, DA) 94 */ 95 96 memcpy(tmp_addr, mac_header->i_addr2, sizeof(tmp_addr)); 97 98 memcpy(mac_header->i_addr2, 99 mac_header->i_addr1, sizeof(tmp_addr)); 100 101 memcpy(mac_header->i_addr1, 102 mac_header->i_addr3, sizeof(tmp_addr)); 103 104 memcpy(mac_header->i_addr3, tmp_addr, sizeof(tmp_addr)); 105 } 106 } 107 108 static inline void ol_rx_fwd_to_tx(struct ol_txrx_vdev_t *vdev, qdf_nbuf_t msdu) 109 { 110 struct ol_txrx_pdev_t *pdev = vdev->pdev; 111 112 if (pdev->frame_format == wlan_frm_fmt_native_wifi) 113 ol_ap_fwd_check(vdev, msdu); 114 115 /* 116 * Map the netbuf, so it's accessible to the DMA that 117 * sends it to the target. 118 */ 119 qdf_nbuf_set_next(msdu, NULL); /* add NULL terminator */ 120 121 /* for HL, point to payload before send to tx again.*/ 122 if (pdev->cfg.is_high_latency) { 123 void *rx_desc; 124 125 rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, 126 msdu); 127 qdf_nbuf_pull_head(msdu, 128 htt_rx_msdu_rx_desc_size_hl(pdev->htt_pdev, 129 rx_desc)); 130 } 131 132 /* Clear the msdu control block as it will be re-interpreted */ 133 qdf_mem_set(msdu->cb, sizeof(msdu->cb), 0); 134 /* update any cb field expected by OL_TX_SEND */ 135 136 msdu = OL_TX_SEND(vdev, msdu); 137 138 if (msdu) { 139 /* 140 * The frame was not accepted by the tx. 141 * We could store the frame and try again later, 142 * but the simplest solution is to discard the frames. 143 */ 144 qdf_nbuf_tx_free(msdu, QDF_NBUF_PKT_ERROR); 145 } 146 } 147 148 void 149 ol_rx_fwd_check(struct ol_txrx_vdev_t *vdev, 150 struct ol_txrx_peer_t *peer, 151 unsigned int tid, qdf_nbuf_t msdu_list) 152 { 153 struct ol_txrx_pdev_t *pdev = vdev->pdev; 154 qdf_nbuf_t deliver_list_head = NULL; 155 qdf_nbuf_t deliver_list_tail = NULL; 156 qdf_nbuf_t msdu; 157 158 msdu = msdu_list; 159 while (msdu) { 160 struct ol_txrx_vdev_t *tx_vdev; 161 void *rx_desc; 162 /* 163 * Remember the next list elem, because our processing 164 * may cause the MSDU to get linked into a different list. 165 */ 166 msdu_list = qdf_nbuf_next(msdu); 167 168 rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu); 169 170 if (!vdev->disable_intrabss_fwd && 171 htt_rx_msdu_forward(pdev->htt_pdev, rx_desc)) { 172 /* 173 * Use the same vdev that received the frame to 174 * transmit the frame. 175 * This is exactly what we want for intra-BSS 176 * forwarding, like STA-to-STA forwarding and 177 * multicast echo. 178 * If this is a intra-BSS forwarding case (which is not 179 * currently supported), then the tx vdev is different 180 * from the rx vdev. 181 * On the LL host the vdevs are not actually used 182 * for tx, so it would still work to use the rx vdev 183 * rather than the tx vdev. 184 * For HL, the tx classification searches for the DA 185 * within the given vdev, so we would want to get the DA 186 * peer ID from the target, so we can locate 187 * the tx vdev. 188 */ 189 tx_vdev = vdev; 190 /* 191 * Copying TID value of RX packet to forwarded 192 * packet if the tid is other than non qos tid. 193 * But for non qos tid fill invalid tid so that 194 * Fw will take care of filling proper tid. 195 */ 196 if (tid != HTT_NON_QOS_TID) { 197 qdf_nbuf_set_tid(msdu, tid); 198 } else { 199 qdf_nbuf_set_tid(msdu, 200 QDF_NBUF_TX_EXT_TID_INVALID); 201 } 202 203 if (!ol_txrx_fwd_desc_thresh_check(vdev)) { 204 /* Drop the packet*/ 205 htt_rx_msdu_desc_free(pdev->htt_pdev, msdu); 206 TXRX_STATS_MSDU_LIST_INCR( 207 pdev, tx.dropped.host_reject, msdu); 208 /* add NULL terminator */ 209 qdf_nbuf_set_next(msdu, NULL); 210 qdf_nbuf_tx_free(msdu, 211 QDF_NBUF_PKT_ERROR); 212 msdu = msdu_list; 213 continue; 214 } 215 216 /* 217 * This MSDU needs to be forwarded to the tx path. 218 * Check whether it also needs to be sent to the OS 219 * shim, in which case we need to make a copy 220 * (or clone?). 221 */ 222 if (htt_rx_msdu_discard(pdev->htt_pdev, rx_desc)) { 223 htt_rx_msdu_desc_free(pdev->htt_pdev, msdu); 224 ol_rx_fwd_to_tx(tx_vdev, msdu); 225 msdu = NULL; /* already handled this MSDU */ 226 tx_vdev->fwd_tx_packets++; 227 vdev->fwd_rx_packets++; 228 TXRX_STATS_ADD(pdev, 229 pub.rx.intra_bss_fwd.packets_fwd, 1); 230 } else { 231 qdf_nbuf_t copy; 232 233 copy = qdf_nbuf_copy(msdu); 234 if (copy) { 235 ol_rx_fwd_to_tx(tx_vdev, copy); 236 tx_vdev->fwd_tx_packets++; 237 } 238 TXRX_STATS_ADD(pdev, 239 pub.rx.intra_bss_fwd.packets_stack_n_fwd, 1); 240 } 241 } else { 242 TXRX_STATS_ADD(pdev, 243 pub.rx.intra_bss_fwd.packets_stack, 1); 244 } 245 if (msdu) { 246 /* send this frame to the OS */ 247 OL_TXRX_LIST_APPEND(deliver_list_head, 248 deliver_list_tail, msdu); 249 } 250 msdu = msdu_list; 251 } 252 if (deliver_list_head) { 253 /* add NULL terminator */ 254 qdf_nbuf_set_next(deliver_list_tail, NULL); 255 if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev)) { 256 ol_rx_in_order_deliver(vdev, peer, tid, 257 deliver_list_head); 258 } else { 259 ol_rx_deliver(vdev, peer, tid, deliver_list_head); 260 } 261 } 262 } 263 264 /* 265 * ol_get_intra_bss_fwd_pkts_count() - to get the total tx and rx packets 266 * that has been forwarded from txrx layer without going to upper layers. 267 * @vdev_id: vdev id 268 * @fwd_tx_packets: pointer to forwarded tx packets count parameter 269 * @fwd_rx_packets: pointer to forwarded rx packets count parameter 270 * 271 * Return: status -> A_OK - success, A_ERROR - failure 272 */ 273 A_STATUS ol_get_intra_bss_fwd_pkts_count(uint8_t vdev_id, 274 uint64_t *fwd_tx_packets, uint64_t *fwd_rx_packets) 275 { 276 struct ol_txrx_vdev_t *vdev = NULL; 277 278 vdev = (struct ol_txrx_vdev_t *)ol_txrx_get_vdev_from_vdev_id(vdev_id); 279 if (!vdev) 280 return A_ERROR; 281 282 *fwd_tx_packets = vdev->fwd_tx_packets; 283 *fwd_rx_packets = vdev->fwd_rx_packets; 284 return A_OK; 285 } 286 287