1 /* 2 * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for 6 * any purpose with or without fee is hereby granted, provided that the 7 * above copyright notice and this permission notice appear in all 8 * copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /*======================================================================== 21 22 \file epping_tx.c 23 24 \brief WLAN End Point Ping test tool implementation 25 26 ========================================================================*/ 27 28 /*-------------------------------------------------------------------------- 29 Include Files 30 ------------------------------------------------------------------------*/ 31 #include <cds_api.h> 32 #include <cds_sched.h> 33 #include <linux/etherdevice.h> 34 #include <linux/firmware.h> 35 #include <wni_api.h> 36 #include <wlan_ptt_sock_svc.h> 37 #include <linux/wireless.h> 38 #include <net/cfg80211.h> 39 #include <linux/rtnetlink.h> 40 #include <linux/semaphore.h> 41 #include <linux/ctype.h> 42 #include "epping_main.h" 43 #include "epping_internal.h" 44 #include "epping_test.h" 45 46 #define TX_RETRY_TIMEOUT_IN_MS 1 47 48 static bool enb_tx_dump; 49 50 void epping_tx_dup_pkt(epping_adapter_t *adapter, 51 HTC_ENDPOINT_ID eid, qdf_nbuf_t skb) 52 { 53 struct epping_cookie *cookie = NULL; 54 int skb_len, ret; 55 qdf_nbuf_t new_skb; 56 57 cookie = epping_alloc_cookie(adapter->pEpping_ctx); 58 if (!cookie) { 59 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 60 "%s: epping_alloc_cookie returns no resource\n", 61 __func__); 62 return; 63 } 64 new_skb = qdf_nbuf_copy(skb); 65 if (!new_skb) { 66 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 67 "%s: qdf_nbuf_copy returns no resource\n", __func__); 68 epping_free_cookie(adapter->pEpping_ctx, cookie); 69 return; 70 } 71 SET_HTC_PACKET_INFO_TX(&cookie->HtcPkt, 72 cookie, qdf_nbuf_data(skb), 73 qdf_nbuf_len(new_skb), eid, 0); 74 SET_HTC_PACKET_NET_BUF_CONTEXT(&cookie->HtcPkt, new_skb); 75 skb_len = (int)qdf_nbuf_len(new_skb); 76 /* send the packet */ 77 ret = htc_send_pkt(adapter->pEpping_ctx->HTCHandle, &cookie->HtcPkt); 78 if (ret != QDF_STATUS_SUCCESS) { 79 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 80 "%s: htc_send_pkt failed, ret = %d\n", __func__, ret); 81 epping_free_cookie(adapter->pEpping_ctx, cookie); 82 qdf_nbuf_free(new_skb); 83 return; 84 } 85 adapter->stats.tx_bytes += skb_len; 86 ++adapter->stats.tx_packets; 87 if (((adapter->stats.tx_packets + 88 adapter->stats.tx_dropped) % EPPING_STATS_LOG_COUNT) == 0 && 89 (adapter->stats.tx_packets || adapter->stats.tx_dropped)) { 90 epping_log_stats(adapter, __func__); 91 } 92 } 93 94 static int epping_tx_send_int(qdf_nbuf_t skb, epping_adapter_t *adapter) 95 { 96 EPPING_HEADER *eppingHdr = (EPPING_HEADER *) qdf_nbuf_data(skb); 97 HTC_ENDPOINT_ID eid = ENDPOINT_UNUSED; 98 struct epping_cookie *cookie = NULL; 99 uint8_t ac = 0; 100 QDF_STATUS ret = QDF_STATUS_SUCCESS; 101 int skb_len; 102 EPPING_HEADER tmpHdr = *eppingHdr; 103 104 /* allocate resource for this packet */ 105 cookie = epping_alloc_cookie(adapter->pEpping_ctx); 106 /* no resource */ 107 if (!cookie) { 108 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 109 "%s: epping_alloc_cookie returns no resource\n", 110 __func__); 111 return A_ERROR; 112 } 113 114 if (enb_tx_dump) 115 epping_hex_dump((void *)eppingHdr, skb->len, __func__); 116 /* 117 * a quirk of linux, the payload of the frame is 32-bit aligned and thus 118 * the addition of the HTC header will mis-align the start of the HTC 119 * frame, so we add some padding which will be stripped off in the target 120 */ 121 if (EPPING_ALIGNMENT_PAD > 0) { 122 A_NETBUF_PUSH(skb, EPPING_ALIGNMENT_PAD); 123 } 124 /* prepare ep/HTC information */ 125 ac = eppingHdr->StreamNo_h; 126 eid = adapter->pEpping_ctx->EppingEndpoint[ac]; 127 if (eid < 0 || eid >= EPPING_MAX_NUM_EPIDS) { 128 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 129 "%s: invalid eid = %d, ac = %d\n", __func__, eid, 130 ac); 131 return A_ERROR; 132 } 133 if (tmpHdr.Cmd_h == EPPING_CMD_RESET_RECV_CNT || 134 tmpHdr.Cmd_h == EPPING_CMD_CONT_RX_START) { 135 epping_set_kperf_flag(adapter, eid, tmpHdr.CmdBuffer_t[0]); 136 } 137 SET_HTC_PACKET_INFO_TX(&cookie->HtcPkt, 138 cookie, qdf_nbuf_data(skb), qdf_nbuf_len(skb), 139 eid, 0); 140 SET_HTC_PACKET_NET_BUF_CONTEXT(&cookie->HtcPkt, skb); 141 skb_len = skb->len; 142 /* send the packet */ 143 ret = htc_send_pkt(adapter->pEpping_ctx->HTCHandle, &cookie->HtcPkt); 144 epping_log_packet(adapter, &tmpHdr, ret, __func__); 145 if (ret != QDF_STATUS_SUCCESS) { 146 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 147 "%s: htc_send_pkt failed, status = %d\n", __func__, 148 ret); 149 epping_free_cookie(adapter->pEpping_ctx, cookie); 150 return A_ERROR; 151 } 152 adapter->stats.tx_bytes += skb_len; 153 ++adapter->stats.tx_packets; 154 if (((adapter->stats.tx_packets + 155 adapter->stats.tx_dropped) % EPPING_STATS_LOG_COUNT) == 0 && 156 (adapter->stats.tx_packets || adapter->stats.tx_dropped)) { 157 epping_log_stats(adapter, __func__); 158 } 159 160 return 0; 161 } 162 163 void epping_tx_timer_expire(epping_adapter_t *adapter) 164 { 165 qdf_nbuf_t nodrop_skb; 166 167 EPPING_LOG(QDF_TRACE_LEVEL_INFO, "%s: queue len: %d\n", __func__, 168 qdf_nbuf_queue_len(&adapter->nodrop_queue)); 169 170 if (!qdf_nbuf_queue_len(&adapter->nodrop_queue)) { 171 /* nodrop queue is empty so no need to arm timer */ 172 adapter->epping_timer_state = EPPING_TX_TIMER_STOPPED; 173 return; 174 } 175 176 /* try to flush nodrop queue */ 177 while ((nodrop_skb = qdf_nbuf_queue_remove(&adapter->nodrop_queue))) { 178 htc_set_nodrop_pkt(adapter->pEpping_ctx->HTCHandle, true); 179 if (epping_tx_send_int(nodrop_skb, adapter)) { 180 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 181 "%s: nodrop: %pK xmit fail in timer\n", 182 __func__, nodrop_skb); 183 /* fail to xmit so put the nodrop packet to the nodrop queue */ 184 qdf_nbuf_queue_insert_head(&adapter->nodrop_queue, 185 nodrop_skb); 186 break; 187 } else { 188 htc_set_nodrop_pkt(adapter->pEpping_ctx->HTCHandle, false); 189 EPPING_LOG(QDF_TRACE_LEVEL_INFO, 190 "%s: nodrop: %pK xmit ok in timer\n", 191 __func__, nodrop_skb); 192 } 193 } 194 195 /* if nodrop queue is not empty, continue to arm timer */ 196 if (nodrop_skb) { 197 qdf_spin_lock_bh(&adapter->data_lock); 198 /* if nodrop queue is not empty, continue to arm timer */ 199 if (adapter->epping_timer_state != EPPING_TX_TIMER_RUNNING) { 200 adapter->epping_timer_state = EPPING_TX_TIMER_RUNNING; 201 qdf_timer_mod(&adapter->epping_timer, 202 TX_RETRY_TIMEOUT_IN_MS); 203 } 204 qdf_spin_unlock_bh(&adapter->data_lock); 205 } else { 206 adapter->epping_timer_state = EPPING_TX_TIMER_STOPPED; 207 } 208 } 209 210 int epping_tx_send(qdf_nbuf_t skb, epping_adapter_t *adapter) 211 { 212 qdf_nbuf_t nodrop_skb; 213 EPPING_HEADER *eppingHdr; 214 uint8_t ac = 0; 215 216 eppingHdr = (EPPING_HEADER *) qdf_nbuf_data(skb); 217 218 if (!IS_EPPING_PACKET(eppingHdr)) { 219 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 220 "%s: Received non endpoint ping packets\n", __func__); 221 /* no packet to send, cleanup */ 222 qdf_nbuf_free(skb); 223 return -ENOMEM; 224 } 225 226 /* the stream ID is mapped to an access class */ 227 ac = eppingHdr->StreamNo_h; 228 /* hard coded two ep ids */ 229 if (ac != 0 && ac != 1) { 230 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 231 "%s: ac %d is not mapped to mboxping service\n", 232 __func__, ac); 233 qdf_nbuf_free(skb); 234 return -ENOMEM; 235 } 236 237 /* 238 * some EPPING packets cannot be dropped no matter what access class 239 * it was sent on. A special care has been taken: 240 * 1. when there is no TX resource, queue the control packets to 241 * a special queue 242 * 2. when there is TX resource, send the queued control packets first 243 * and then other packets 244 * 3. a timer launches to check if there is queued control packets and 245 * flush them 246 */ 247 248 /* check the nodrop queue first */ 249 while ((nodrop_skb = qdf_nbuf_queue_remove(&adapter->nodrop_queue))) { 250 htc_set_nodrop_pkt(adapter->pEpping_ctx->HTCHandle, true); 251 if (epping_tx_send_int(nodrop_skb, adapter)) { 252 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 253 "%s: nodrop: %pK xmit fail\n", __func__, 254 nodrop_skb); 255 /* fail to xmit so put the nodrop packet to the nodrop queue */ 256 qdf_nbuf_queue_insert_head(&adapter->nodrop_queue, 257 nodrop_skb); 258 /* no cookie so free the current skb */ 259 goto tx_fail; 260 } else { 261 htc_set_nodrop_pkt(adapter->pEpping_ctx->HTCHandle, false); 262 EPPING_LOG(QDF_TRACE_LEVEL_INFO, 263 "%s: nodrop: %pK xmit ok\n", __func__, 264 nodrop_skb); 265 } 266 } 267 268 /* send the original packet */ 269 if (epping_tx_send_int(skb, adapter)) 270 goto tx_fail; 271 272 return 0; 273 274 tx_fail: 275 if (!IS_EPING_PACKET_NO_DROP(eppingHdr)) { 276 /* allow to drop the skb so drop it */ 277 qdf_nbuf_free(skb); 278 ++adapter->stats.tx_dropped; 279 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 280 "%s: Tx skb %pK dropped, stats.tx_dropped = %ld\n", 281 __func__, skb, adapter->stats.tx_dropped); 282 return -ENOMEM; 283 } else { 284 EPPING_LOG(QDF_TRACE_LEVEL_FATAL, 285 "%s: nodrop: %pK queued\n", __func__, skb); 286 qdf_nbuf_queue_add(&adapter->nodrop_queue, skb); 287 qdf_spin_lock_bh(&adapter->data_lock); 288 if (adapter->epping_timer_state != EPPING_TX_TIMER_RUNNING) { 289 adapter->epping_timer_state = EPPING_TX_TIMER_RUNNING; 290 qdf_timer_mod(&adapter->epping_timer, 291 TX_RETRY_TIMEOUT_IN_MS); 292 } 293 qdf_spin_unlock_bh(&adapter->data_lock); 294 } 295 296 return 0; 297 } 298 299 #ifdef HIF_SDIO 300 enum htc_send_full_action epping_tx_queue_full(void *Context, 301 HTC_PACKET *pPacket) 302 { 303 /* 304 * Call netif_stop_queue frequently will impact the mboxping tx t-put. 305 * Return HTC_SEND_FULL_KEEP directly in epping_tx_queue_full to avoid. 306 */ 307 return HTC_SEND_FULL_KEEP; 308 } 309 #endif /* HIF_SDIO */ 310 void epping_tx_complete(void *ctx, HTC_PACKET *htc_pkt) 311 { 312 epping_context_t *pEpping_ctx = (epping_context_t *) ctx; 313 epping_adapter_t *adapter = pEpping_ctx->epping_adapter; 314 struct net_device *dev = adapter->dev; 315 QDF_STATUS status; 316 HTC_ENDPOINT_ID eid; 317 qdf_nbuf_t pktSkb; 318 struct epping_cookie *cookie; 319 A_BOOL flushing = false; 320 qdf_nbuf_queue_t skb_queue; 321 322 if (!htc_pkt) 323 return; 324 325 qdf_nbuf_queue_init(&skb_queue); 326 327 qdf_spin_lock_bh(&adapter->data_lock); 328 329 status = htc_pkt->Status; 330 eid = htc_pkt->Endpoint; 331 pktSkb = GET_HTC_PACKET_NET_BUF_CONTEXT(htc_pkt); 332 cookie = htc_pkt->pPktContext; 333 334 if (!pktSkb) { 335 EPPING_LOG(QDF_TRACE_LEVEL_ERROR, 336 "%s: NULL skb from hc packet", __func__); 337 QDF_BUG(0); 338 } else { 339 if (htc_pkt->pBuffer != qdf_nbuf_data(pktSkb)) { 340 EPPING_LOG(QDF_TRACE_LEVEL_ERROR, 341 "%s: htc_pkt buffer not equal to skb->data", 342 __func__); 343 QDF_BUG(0); 344 } 345 /* add this to the list, use faster non-lock API */ 346 qdf_nbuf_queue_add(&skb_queue, pktSkb); 347 348 if (QDF_IS_STATUS_SUCCESS(status)) { 349 if (htc_pkt->ActualLength != 350 qdf_nbuf_len(pktSkb)) { 351 EPPING_LOG(QDF_TRACE_LEVEL_ERROR, 352 "%s: htc_pkt length not equal to skb->len", 353 __func__); 354 QDF_BUG(0); 355 } 356 } 357 } 358 359 EPPING_LOG(QDF_TRACE_LEVEL_INFO, 360 "%s skb=%pK data=%pK len=0x%x eid=%d ", 361 __func__, pktSkb, htc_pkt->pBuffer, 362 htc_pkt->ActualLength, eid); 363 364 if (QDF_IS_STATUS_ERROR(status)) { 365 if (status == QDF_STATUS_E_CANCELED) { 366 /* a packet was flushed */ 367 flushing = true; 368 } 369 if (status != QDF_STATUS_E_RESOURCES) { 370 EPPING_LOG(QDF_TRACE_LEVEL_ERROR, 371 "%s() -TX ERROR, status: 0x%x", 372 __func__, status); 373 } 374 } else { 375 EPPING_LOG(QDF_TRACE_LEVEL_INFO, "%s: OK\n", __func__); 376 flushing = false; 377 } 378 379 epping_free_cookie(adapter->pEpping_ctx, cookie); 380 qdf_spin_unlock_bh(&adapter->data_lock); 381 382 /* free all skbs in our local list */ 383 while (qdf_nbuf_queue_len(&skb_queue)) { 384 /* use non-lock version */ 385 pktSkb = qdf_nbuf_queue_remove(&skb_queue); 386 if (!pktSkb) 387 break; 388 qdf_nbuf_tx_free(pktSkb, QDF_NBUF_PKT_ERROR); 389 pEpping_ctx->total_tx_acks++; 390 } 391 392 if (!flushing) { 393 netif_wake_queue(dev); 394 } 395 } 396