162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. 462306a36Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/etherdevice.h> 862306a36Sopenharmony_ci#include <net/ieee80211_radiotap.h> 962306a36Sopenharmony_ci#include <linux/if_arp.h> 1062306a36Sopenharmony_ci#include <linux/moduleparam.h> 1162306a36Sopenharmony_ci#include <linux/ip.h> 1262306a36Sopenharmony_ci#include <linux/ipv6.h> 1362306a36Sopenharmony_ci#include <linux/if_vlan.h> 1462306a36Sopenharmony_ci#include <net/ipv6.h> 1562306a36Sopenharmony_ci#include <linux/prefetch.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "wil6210.h" 1862306a36Sopenharmony_ci#include "wmi.h" 1962306a36Sopenharmony_ci#include "txrx.h" 2062306a36Sopenharmony_ci#include "trace.h" 2162306a36Sopenharmony_ci#include "txrx_edma.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cibool rx_align_2; 2462306a36Sopenharmony_cimodule_param(rx_align_2, bool, 0444); 2562306a36Sopenharmony_ciMODULE_PARM_DESC(rx_align_2, " align Rx buffers on 4*n+2, default - no"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cibool rx_large_buf; 2862306a36Sopenharmony_cimodule_param(rx_large_buf, bool, 0444); 2962306a36Sopenharmony_ciMODULE_PARM_DESC(rx_large_buf, " allocate 8KB RX buffers, default - no"); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Drop Tx packets in case Tx ring is full */ 3262306a36Sopenharmony_cibool drop_if_ring_full; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic inline uint wil_rx_snaplen(void) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return rx_align_2 ? 6 : 0; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* wil_ring_wmark_low - low watermark for available descriptor space */ 4062306a36Sopenharmony_cistatic inline int wil_ring_wmark_low(struct wil_ring *ring) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci return ring->size / 8; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* wil_ring_wmark_high - high watermark for available descriptor space */ 4662306a36Sopenharmony_cistatic inline int wil_ring_wmark_high(struct wil_ring *ring) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci return ring->size / 4; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* returns true if num avail descriptors is lower than wmark_low */ 5262306a36Sopenharmony_cistatic inline int wil_ring_avail_low(struct wil_ring *ring) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci return wil_ring_avail_tx(ring) < wil_ring_wmark_low(ring); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* returns true if num avail descriptors is higher than wmark_high */ 5862306a36Sopenharmony_cistatic inline int wil_ring_avail_high(struct wil_ring *ring) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci return wil_ring_avail_tx(ring) > wil_ring_wmark_high(ring); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* returns true when all tx vrings are empty */ 6462306a36Sopenharmony_cibool wil_is_tx_idle(struct wil6210_priv *wil) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci int i; 6762306a36Sopenharmony_ci unsigned long data_comp_to; 6862306a36Sopenharmony_ci int min_ring_id = wil_get_min_tx_ring_id(wil); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { 7162306a36Sopenharmony_ci struct wil_ring *vring = &wil->ring_tx[i]; 7262306a36Sopenharmony_ci int vring_index = vring - wil->ring_tx; 7362306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = 7462306a36Sopenharmony_ci &wil->ring_tx_data[vring_index]; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci spin_lock(&txdata->lock); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!vring->va || !txdata->enabled) { 7962306a36Sopenharmony_ci spin_unlock(&txdata->lock); 8062306a36Sopenharmony_ci continue; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci data_comp_to = jiffies + msecs_to_jiffies( 8462306a36Sopenharmony_ci WIL_DATA_COMPLETION_TO_MS); 8562306a36Sopenharmony_ci if (test_bit(wil_status_napi_en, wil->status)) { 8662306a36Sopenharmony_ci while (!wil_ring_is_empty(vring)) { 8762306a36Sopenharmony_ci if (time_after(jiffies, data_comp_to)) { 8862306a36Sopenharmony_ci wil_dbg_pm(wil, 8962306a36Sopenharmony_ci "TO waiting for idle tx\n"); 9062306a36Sopenharmony_ci spin_unlock(&txdata->lock); 9162306a36Sopenharmony_ci return false; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci wil_dbg_ratelimited(wil, 9462306a36Sopenharmony_ci "tx vring is not empty -> NAPI\n"); 9562306a36Sopenharmony_ci spin_unlock(&txdata->lock); 9662306a36Sopenharmony_ci napi_synchronize(&wil->napi_tx); 9762306a36Sopenharmony_ci msleep(20); 9862306a36Sopenharmony_ci spin_lock(&txdata->lock); 9962306a36Sopenharmony_ci if (!vring->va || !txdata->enabled) 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci spin_unlock(&txdata->lock); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return true; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int wil_vring_alloc(struct wil6210_priv *wil, struct wil_ring *vring) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 11362306a36Sopenharmony_ci size_t sz = vring->size * sizeof(vring->va[0]); 11462306a36Sopenharmony_ci uint i; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci wil_dbg_misc(wil, "vring_alloc:\n"); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(vring->va[0]) != 32); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci vring->swhead = 0; 12162306a36Sopenharmony_ci vring->swtail = 0; 12262306a36Sopenharmony_ci vring->ctx = kcalloc(vring->size, sizeof(vring->ctx[0]), GFP_KERNEL); 12362306a36Sopenharmony_ci if (!vring->ctx) { 12462306a36Sopenharmony_ci vring->va = NULL; 12562306a36Sopenharmony_ci return -ENOMEM; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* vring->va should be aligned on its size rounded up to power of 2 12962306a36Sopenharmony_ci * This is granted by the dma_alloc_coherent. 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * HW has limitation that all vrings addresses must share the same 13262306a36Sopenharmony_ci * upper 16 msb bits part of 48 bits address. To workaround that, 13362306a36Sopenharmony_ci * if we are using more than 32 bit addresses switch to 32 bit 13462306a36Sopenharmony_ci * allocation before allocating vring memory. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * There's no check for the return value of dma_set_mask_and_coherent, 13762306a36Sopenharmony_ci * since we assume if we were able to set the mask during 13862306a36Sopenharmony_ci * initialization in this system it will not fail if we set it again 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci if (wil->dma_addr_size > 32) 14162306a36Sopenharmony_ci dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); 14462306a36Sopenharmony_ci if (!vring->va) { 14562306a36Sopenharmony_ci kfree(vring->ctx); 14662306a36Sopenharmony_ci vring->ctx = NULL; 14762306a36Sopenharmony_ci return -ENOMEM; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (wil->dma_addr_size > 32) 15162306a36Sopenharmony_ci dma_set_mask_and_coherent(dev, 15262306a36Sopenharmony_ci DMA_BIT_MASK(wil->dma_addr_size)); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* initially, all descriptors are SW owned 15562306a36Sopenharmony_ci * For Tx and Rx, ownership bit is at the same location, thus 15662306a36Sopenharmony_ci * we can use any 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci for (i = 0; i < vring->size; i++) { 15962306a36Sopenharmony_ci volatile struct vring_tx_desc *_d = 16062306a36Sopenharmony_ci &vring->va[i].tx.legacy; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci _d->dma.status = TX_DMA_STATUS_DU; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci wil_dbg_misc(wil, "vring[%d] 0x%p:%pad 0x%p\n", vring->size, 16662306a36Sopenharmony_ci vring->va, &vring->pa, vring->ctx); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void wil_txdesc_unmap(struct device *dev, union wil_tx_desc *desc, 17262306a36Sopenharmony_ci struct wil_ctx *ctx) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct vring_tx_desc *d = &desc->legacy; 17562306a36Sopenharmony_ci dma_addr_t pa = wil_desc_addr(&d->dma.addr); 17662306a36Sopenharmony_ci u16 dmalen = le16_to_cpu(d->dma.length); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci switch (ctx->mapped_as) { 17962306a36Sopenharmony_ci case wil_mapped_as_single: 18062306a36Sopenharmony_ci dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci case wil_mapped_as_page: 18362306a36Sopenharmony_ci dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci default: 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void wil_vring_free(struct wil6210_priv *wil, struct wil_ring *vring) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 19362306a36Sopenharmony_ci size_t sz = vring->size * sizeof(vring->va[0]); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci lockdep_assert_held(&wil->mutex); 19662306a36Sopenharmony_ci if (!vring->is_rx) { 19762306a36Sopenharmony_ci int vring_index = vring - wil->ring_tx; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci wil_dbg_misc(wil, "free Tx vring %d [%d] 0x%p:%pad 0x%p\n", 20062306a36Sopenharmony_ci vring_index, vring->size, vring->va, 20162306a36Sopenharmony_ci &vring->pa, vring->ctx); 20262306a36Sopenharmony_ci } else { 20362306a36Sopenharmony_ci wil_dbg_misc(wil, "free Rx vring [%d] 0x%p:%pad 0x%p\n", 20462306a36Sopenharmony_ci vring->size, vring->va, 20562306a36Sopenharmony_ci &vring->pa, vring->ctx); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci while (!wil_ring_is_empty(vring)) { 20962306a36Sopenharmony_ci dma_addr_t pa; 21062306a36Sopenharmony_ci u16 dmalen; 21162306a36Sopenharmony_ci struct wil_ctx *ctx; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (!vring->is_rx) { 21462306a36Sopenharmony_ci struct vring_tx_desc dd, *d = ⅆ 21562306a36Sopenharmony_ci volatile struct vring_tx_desc *_d = 21662306a36Sopenharmony_ci &vring->va[vring->swtail].tx.legacy; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci ctx = &vring->ctx[vring->swtail]; 21962306a36Sopenharmony_ci if (!ctx) { 22062306a36Sopenharmony_ci wil_dbg_txrx(wil, 22162306a36Sopenharmony_ci "ctx(%d) was already completed\n", 22262306a36Sopenharmony_ci vring->swtail); 22362306a36Sopenharmony_ci vring->swtail = wil_ring_next_tail(vring); 22462306a36Sopenharmony_ci continue; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci *d = *_d; 22762306a36Sopenharmony_ci wil_txdesc_unmap(dev, (union wil_tx_desc *)d, ctx); 22862306a36Sopenharmony_ci if (ctx->skb) 22962306a36Sopenharmony_ci dev_kfree_skb_any(ctx->skb); 23062306a36Sopenharmony_ci vring->swtail = wil_ring_next_tail(vring); 23162306a36Sopenharmony_ci } else { /* rx */ 23262306a36Sopenharmony_ci struct vring_rx_desc dd, *d = ⅆ 23362306a36Sopenharmony_ci volatile struct vring_rx_desc *_d = 23462306a36Sopenharmony_ci &vring->va[vring->swhead].rx.legacy; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ctx = &vring->ctx[vring->swhead]; 23762306a36Sopenharmony_ci *d = *_d; 23862306a36Sopenharmony_ci pa = wil_desc_addr(&d->dma.addr); 23962306a36Sopenharmony_ci dmalen = le16_to_cpu(d->dma.length); 24062306a36Sopenharmony_ci dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE); 24162306a36Sopenharmony_ci kfree_skb(ctx->skb); 24262306a36Sopenharmony_ci wil_ring_advance_head(vring, 1); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci dma_free_coherent(dev, sz, (void *)vring->va, vring->pa); 24662306a36Sopenharmony_ci kfree(vring->ctx); 24762306a36Sopenharmony_ci vring->pa = 0; 24862306a36Sopenharmony_ci vring->va = NULL; 24962306a36Sopenharmony_ci vring->ctx = NULL; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* Allocate one skb for Rx VRING 25362306a36Sopenharmony_ci * 25462306a36Sopenharmony_ci * Safe to call from IRQ 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_cistatic int wil_vring_alloc_skb(struct wil6210_priv *wil, struct wil_ring *vring, 25762306a36Sopenharmony_ci u32 i, int headroom) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 26062306a36Sopenharmony_ci unsigned int sz = wil->rx_buf_len + ETH_HLEN + wil_rx_snaplen(); 26162306a36Sopenharmony_ci struct vring_rx_desc dd, *d = ⅆ 26262306a36Sopenharmony_ci volatile struct vring_rx_desc *_d = &vring->va[i].rx.legacy; 26362306a36Sopenharmony_ci dma_addr_t pa; 26462306a36Sopenharmony_ci struct sk_buff *skb = dev_alloc_skb(sz + headroom); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (unlikely(!skb)) 26762306a36Sopenharmony_ci return -ENOMEM; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci skb_reserve(skb, headroom); 27062306a36Sopenharmony_ci skb_put(skb, sz); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /** 27362306a36Sopenharmony_ci * Make sure that the network stack calculates checksum for packets 27462306a36Sopenharmony_ci * which failed the HW checksum calculation 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); 27962306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, pa))) { 28062306a36Sopenharmony_ci kfree_skb(skb); 28162306a36Sopenharmony_ci return -ENOMEM; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci d->dma.d0 = RX_DMA_D0_CMD_DMA_RT | RX_DMA_D0_CMD_DMA_IT; 28562306a36Sopenharmony_ci wil_desc_addr_set(&d->dma.addr, pa); 28662306a36Sopenharmony_ci /* ip_length don't care */ 28762306a36Sopenharmony_ci /* b11 don't care */ 28862306a36Sopenharmony_ci /* error don't care */ 28962306a36Sopenharmony_ci d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ 29062306a36Sopenharmony_ci d->dma.length = cpu_to_le16(sz); 29162306a36Sopenharmony_ci *_d = *d; 29262306a36Sopenharmony_ci vring->ctx[i].skb = skb; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* Adds radiotap header 29862306a36Sopenharmony_ci * 29962306a36Sopenharmony_ci * Any error indicated as "Bad FCS" 30062306a36Sopenharmony_ci * 30162306a36Sopenharmony_ci * Vendor data for 04:ce:14-1 (Wilocity-1) consists of: 30262306a36Sopenharmony_ci * - Rx descriptor: 32 bytes 30362306a36Sopenharmony_ci * - Phy info 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic void wil_rx_add_radiotap_header(struct wil6210_priv *wil, 30662306a36Sopenharmony_ci struct sk_buff *skb) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct wil6210_rtap { 30962306a36Sopenharmony_ci struct ieee80211_radiotap_header rthdr; 31062306a36Sopenharmony_ci /* fields should be in the order of bits in rthdr.it_present */ 31162306a36Sopenharmony_ci /* flags */ 31262306a36Sopenharmony_ci u8 flags; 31362306a36Sopenharmony_ci /* channel */ 31462306a36Sopenharmony_ci __le16 chnl_freq __aligned(2); 31562306a36Sopenharmony_ci __le16 chnl_flags; 31662306a36Sopenharmony_ci /* MCS */ 31762306a36Sopenharmony_ci u8 mcs_present; 31862306a36Sopenharmony_ci u8 mcs_flags; 31962306a36Sopenharmony_ci u8 mcs_index; 32062306a36Sopenharmony_ci } __packed; 32162306a36Sopenharmony_ci struct vring_rx_desc *d = wil_skb_rxdesc(skb); 32262306a36Sopenharmony_ci struct wil6210_rtap *rtap; 32362306a36Sopenharmony_ci int rtap_len = sizeof(struct wil6210_rtap); 32462306a36Sopenharmony_ci struct ieee80211_channel *ch = wil->monitor_chandef.chan; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (skb_headroom(skb) < rtap_len && 32762306a36Sopenharmony_ci pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) { 32862306a36Sopenharmony_ci wil_err(wil, "Unable to expand headroom to %d\n", rtap_len); 32962306a36Sopenharmony_ci return; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci rtap = skb_push(skb, rtap_len); 33362306a36Sopenharmony_ci memset(rtap, 0, rtap_len); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci rtap->rthdr.it_version = PKTHDR_RADIOTAP_VERSION; 33662306a36Sopenharmony_ci rtap->rthdr.it_len = cpu_to_le16(rtap_len); 33762306a36Sopenharmony_ci rtap->rthdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | 33862306a36Sopenharmony_ci (1 << IEEE80211_RADIOTAP_CHANNEL) | 33962306a36Sopenharmony_ci (1 << IEEE80211_RADIOTAP_MCS)); 34062306a36Sopenharmony_ci if (d->dma.status & RX_DMA_STATUS_ERROR) 34162306a36Sopenharmony_ci rtap->flags |= IEEE80211_RADIOTAP_F_BADFCS; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci rtap->chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320); 34462306a36Sopenharmony_ci rtap->chnl_flags = cpu_to_le16(0); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci rtap->mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS; 34762306a36Sopenharmony_ci rtap->mcs_flags = 0; 34862306a36Sopenharmony_ci rtap->mcs_index = wil_rxdesc_mcs(d); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic bool wil_is_rx_idle(struct wil6210_priv *wil) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct vring_rx_desc *_d; 35462306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_rx; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci _d = (struct vring_rx_desc *)&ring->va[ring->swhead].rx.legacy; 35762306a36Sopenharmony_ci if (_d->dma.status & RX_DMA_STATUS_DU) 35862306a36Sopenharmony_ci return false; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return true; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int wil_rx_get_cid_by_skb(struct wil6210_priv *wil, struct sk_buff *skb) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct vring_rx_desc *d = wil_skb_rxdesc(skb); 36662306a36Sopenharmony_ci int mid = wil_rxdesc_mid(d); 36762306a36Sopenharmony_ci struct wil6210_vif *vif = wil->vifs[mid]; 36862306a36Sopenharmony_ci /* cid from DMA descriptor is limited to 3 bits. 36962306a36Sopenharmony_ci * In case of cid>=8, the value would be cid modulo 8 and we need to 37062306a36Sopenharmony_ci * find real cid by locating the transmitter (ta) inside sta array 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci int cid = wil_rxdesc_cid(d); 37362306a36Sopenharmony_ci unsigned int snaplen = wil_rx_snaplen(); 37462306a36Sopenharmony_ci struct ieee80211_hdr_3addr *hdr; 37562306a36Sopenharmony_ci int i; 37662306a36Sopenharmony_ci unsigned char *ta; 37762306a36Sopenharmony_ci u8 ftype; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* in monitor mode there are no connections */ 38062306a36Sopenharmony_ci if (vif->wdev.iftype == NL80211_IFTYPE_MONITOR) 38162306a36Sopenharmony_ci return cid; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ftype = wil_rxdesc_ftype(d) << 2; 38462306a36Sopenharmony_ci if (likely(ftype == IEEE80211_FTYPE_DATA)) { 38562306a36Sopenharmony_ci if (unlikely(skb->len < ETH_HLEN + snaplen)) { 38662306a36Sopenharmony_ci wil_err_ratelimited(wil, 38762306a36Sopenharmony_ci "Short data frame, len = %d\n", 38862306a36Sopenharmony_ci skb->len); 38962306a36Sopenharmony_ci return -ENOENT; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci ta = wil_skb_get_sa(skb); 39262306a36Sopenharmony_ci } else { 39362306a36Sopenharmony_ci if (unlikely(skb->len < sizeof(struct ieee80211_hdr_3addr))) { 39462306a36Sopenharmony_ci wil_err_ratelimited(wil, "Short frame, len = %d\n", 39562306a36Sopenharmony_ci skb->len); 39662306a36Sopenharmony_ci return -ENOENT; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci hdr = (void *)skb->data; 39962306a36Sopenharmony_ci ta = hdr->addr2; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (wil->max_assoc_sta <= WIL6210_RX_DESC_MAX_CID) 40362306a36Sopenharmony_ci return cid; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* assuming no concurrency between AP interfaces and STA interfaces. 40662306a36Sopenharmony_ci * multista is used only in P2P_GO or AP mode. In other modes return 40762306a36Sopenharmony_ci * cid from the rx descriptor 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci if (vif->wdev.iftype != NL80211_IFTYPE_P2P_GO && 41062306a36Sopenharmony_ci vif->wdev.iftype != NL80211_IFTYPE_AP) 41162306a36Sopenharmony_ci return cid; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* For Rx packets cid from rx descriptor is limited to 3 bits (0..7), 41462306a36Sopenharmony_ci * to find the real cid, compare transmitter address with the stored 41562306a36Sopenharmony_ci * stations mac address in the driver sta array 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci for (i = cid; i < wil->max_assoc_sta; i += WIL6210_RX_DESC_MAX_CID) { 41862306a36Sopenharmony_ci if (wil->sta[i].status != wil_sta_unused && 41962306a36Sopenharmony_ci ether_addr_equal(wil->sta[i].addr, ta)) { 42062306a36Sopenharmony_ci cid = i; 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci if (i >= wil->max_assoc_sta) { 42562306a36Sopenharmony_ci wil_err_ratelimited(wil, "Could not find cid for frame with transmit addr = %pM, iftype = %d, frametype = %d, len = %d\n", 42662306a36Sopenharmony_ci ta, vif->wdev.iftype, ftype, skb->len); 42762306a36Sopenharmony_ci cid = -ENOENT; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return cid; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/* reap 1 frame from @swhead 43462306a36Sopenharmony_ci * 43562306a36Sopenharmony_ci * Rx descriptor copied to skb->cb 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Safe to call from IRQ 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_cistatic struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, 44062306a36Sopenharmony_ci struct wil_ring *vring) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 44362306a36Sopenharmony_ci struct wil6210_vif *vif; 44462306a36Sopenharmony_ci struct net_device *ndev; 44562306a36Sopenharmony_ci volatile struct vring_rx_desc *_d; 44662306a36Sopenharmony_ci struct vring_rx_desc *d; 44762306a36Sopenharmony_ci struct sk_buff *skb; 44862306a36Sopenharmony_ci dma_addr_t pa; 44962306a36Sopenharmony_ci unsigned int snaplen = wil_rx_snaplen(); 45062306a36Sopenharmony_ci unsigned int sz = wil->rx_buf_len + ETH_HLEN + snaplen; 45162306a36Sopenharmony_ci u16 dmalen; 45262306a36Sopenharmony_ci u8 ftype; 45362306a36Sopenharmony_ci int cid, mid; 45462306a36Sopenharmony_ci int i; 45562306a36Sopenharmony_ci struct wil_net_stats *stats; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct skb_rx_info) > sizeof(skb->cb)); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ciagain: 46062306a36Sopenharmony_ci if (unlikely(wil_ring_is_empty(vring))) 46162306a36Sopenharmony_ci return NULL; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci i = (int)vring->swhead; 46462306a36Sopenharmony_ci _d = &vring->va[i].rx.legacy; 46562306a36Sopenharmony_ci if (unlikely(!(_d->dma.status & RX_DMA_STATUS_DU))) { 46662306a36Sopenharmony_ci /* it is not error, we just reached end of Rx done area */ 46762306a36Sopenharmony_ci return NULL; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci skb = vring->ctx[i].skb; 47162306a36Sopenharmony_ci vring->ctx[i].skb = NULL; 47262306a36Sopenharmony_ci wil_ring_advance_head(vring, 1); 47362306a36Sopenharmony_ci if (!skb) { 47462306a36Sopenharmony_ci wil_err(wil, "No Rx skb at [%d]\n", i); 47562306a36Sopenharmony_ci goto again; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci d = wil_skb_rxdesc(skb); 47862306a36Sopenharmony_ci *d = *_d; 47962306a36Sopenharmony_ci pa = wil_desc_addr(&d->dma.addr); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); 48262306a36Sopenharmony_ci dmalen = le16_to_cpu(d->dma.length); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci trace_wil6210_rx(i, d); 48562306a36Sopenharmony_ci wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", i, dmalen); 48662306a36Sopenharmony_ci wil_hex_dump_txrx("RxD ", DUMP_PREFIX_NONE, 32, 4, 48762306a36Sopenharmony_ci (const void *)d, sizeof(*d), false); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci mid = wil_rxdesc_mid(d); 49062306a36Sopenharmony_ci vif = wil->vifs[mid]; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (unlikely(!vif)) { 49362306a36Sopenharmony_ci wil_dbg_txrx(wil, "skipped RX descriptor with invalid mid %d", 49462306a36Sopenharmony_ci mid); 49562306a36Sopenharmony_ci kfree_skb(skb); 49662306a36Sopenharmony_ci goto again; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci ndev = vif_to_ndev(vif); 49962306a36Sopenharmony_ci if (unlikely(dmalen > sz)) { 50062306a36Sopenharmony_ci wil_err_ratelimited(wil, "Rx size too large: %d bytes!\n", 50162306a36Sopenharmony_ci dmalen); 50262306a36Sopenharmony_ci kfree_skb(skb); 50362306a36Sopenharmony_ci goto again; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci skb_trim(skb, dmalen); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci prefetch(skb->data); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, 51062306a36Sopenharmony_ci skb->data, skb_headlen(skb), false); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci cid = wil_rx_get_cid_by_skb(wil, skb); 51362306a36Sopenharmony_ci if (cid == -ENOENT) { 51462306a36Sopenharmony_ci kfree_skb(skb); 51562306a36Sopenharmony_ci goto again; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci wil_skb_set_cid(skb, (u8)cid); 51862306a36Sopenharmony_ci stats = &wil->sta[cid].stats; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci stats->last_mcs_rx = wil_rxdesc_mcs(d); 52162306a36Sopenharmony_ci if (stats->last_mcs_rx < ARRAY_SIZE(stats->rx_per_mcs)) 52262306a36Sopenharmony_ci stats->rx_per_mcs[stats->last_mcs_rx]++; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* use radiotap header only if required */ 52562306a36Sopenharmony_ci if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) 52662306a36Sopenharmony_ci wil_rx_add_radiotap_header(wil, skb); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* no extra checks if in sniffer mode */ 52962306a36Sopenharmony_ci if (ndev->type != ARPHRD_ETHER) 53062306a36Sopenharmony_ci return skb; 53162306a36Sopenharmony_ci /* Non-data frames may be delivered through Rx DMA channel (ex: BAR) 53262306a36Sopenharmony_ci * Driver should recognize it by frame type, that is found 53362306a36Sopenharmony_ci * in Rx descriptor. If type is not data, it is 802.11 frame as is 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci ftype = wil_rxdesc_ftype(d) << 2; 53662306a36Sopenharmony_ci if (unlikely(ftype != IEEE80211_FTYPE_DATA)) { 53762306a36Sopenharmony_ci u8 fc1 = wil_rxdesc_fc1(d); 53862306a36Sopenharmony_ci int tid = wil_rxdesc_tid(d); 53962306a36Sopenharmony_ci u16 seq = wil_rxdesc_seq(d); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci wil_dbg_txrx(wil, 54262306a36Sopenharmony_ci "Non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n", 54362306a36Sopenharmony_ci fc1, mid, cid, tid, seq); 54462306a36Sopenharmony_ci stats->rx_non_data_frame++; 54562306a36Sopenharmony_ci if (wil_is_back_req(fc1)) { 54662306a36Sopenharmony_ci wil_dbg_txrx(wil, 54762306a36Sopenharmony_ci "BAR: MID %d CID %d TID %d Seq 0x%03x\n", 54862306a36Sopenharmony_ci mid, cid, tid, seq); 54962306a36Sopenharmony_ci wil_rx_bar(wil, vif, cid, tid, seq); 55062306a36Sopenharmony_ci } else { 55162306a36Sopenharmony_ci /* print again all info. One can enable only this 55262306a36Sopenharmony_ci * without overhead for printing every Rx frame 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci wil_dbg_txrx(wil, 55562306a36Sopenharmony_ci "Unhandled non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n", 55662306a36Sopenharmony_ci fc1, mid, cid, tid, seq); 55762306a36Sopenharmony_ci wil_hex_dump_txrx("RxD ", DUMP_PREFIX_NONE, 32, 4, 55862306a36Sopenharmony_ci (const void *)d, sizeof(*d), false); 55962306a36Sopenharmony_ci wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, 56062306a36Sopenharmony_ci skb->data, skb_headlen(skb), false); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci kfree_skb(skb); 56362306a36Sopenharmony_ci goto again; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* L4 IDENT is on when HW calculated checksum, check status 56762306a36Sopenharmony_ci * and in case of error drop the packet 56862306a36Sopenharmony_ci * higher stack layers will handle retransmission (if required) 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci if (likely(d->dma.status & RX_DMA_STATUS_L4I)) { 57162306a36Sopenharmony_ci /* L4 protocol identified, csum calculated */ 57262306a36Sopenharmony_ci if (likely((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0)) 57362306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 57462306a36Sopenharmony_ci /* If HW reports bad checksum, let IP stack re-check it 57562306a36Sopenharmony_ci * For example, HW don't understand Microsoft IP stack that 57662306a36Sopenharmony_ci * mis-calculates TCP checksum - if it should be 0x0, 57762306a36Sopenharmony_ci * it writes 0xffff in violation of RFC 1624 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci else 58062306a36Sopenharmony_ci stats->rx_csum_err++; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (snaplen) { 58462306a36Sopenharmony_ci /* Packet layout 58562306a36Sopenharmony_ci * +-------+-------+---------+------------+------+ 58662306a36Sopenharmony_ci * | SA(6) | DA(6) | SNAP(6) | ETHTYPE(2) | DATA | 58762306a36Sopenharmony_ci * +-------+-------+---------+------------+------+ 58862306a36Sopenharmony_ci * Need to remove SNAP, shifting SA and DA forward 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci memmove(skb->data + snaplen, skb->data, 2 * ETH_ALEN); 59162306a36Sopenharmony_ci skb_pull(skb, snaplen); 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci return skb; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/* allocate and fill up to @count buffers in rx ring 59862306a36Sopenharmony_ci * buffers posted at @swtail 59962306a36Sopenharmony_ci * Note: we have a single RX queue for servicing all VIFs, but we 60062306a36Sopenharmony_ci * allocate skbs with headroom according to main interface only. This 60162306a36Sopenharmony_ci * means it will not work with monitor interface together with other VIFs. 60262306a36Sopenharmony_ci * Currently we only support monitor interface on its own without other VIFs, 60362306a36Sopenharmony_ci * and we will need to fix this code once we add support. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_cistatic int wil_rx_refill(struct wil6210_priv *wil, int count) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct net_device *ndev = wil->main_ndev; 60862306a36Sopenharmony_ci struct wil_ring *v = &wil->ring_rx; 60962306a36Sopenharmony_ci u32 next_tail; 61062306a36Sopenharmony_ci int rc = 0; 61162306a36Sopenharmony_ci int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ? 61262306a36Sopenharmony_ci WIL6210_RTAP_SIZE : 0; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci for (; next_tail = wil_ring_next_tail(v), 61562306a36Sopenharmony_ci (next_tail != v->swhead) && (count-- > 0); 61662306a36Sopenharmony_ci v->swtail = next_tail) { 61762306a36Sopenharmony_ci rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom); 61862306a36Sopenharmony_ci if (unlikely(rc)) { 61962306a36Sopenharmony_ci wil_err_ratelimited(wil, "Error %d in rx refill[%d]\n", 62062306a36Sopenharmony_ci rc, v->swtail); 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* make sure all writes to descriptors (shared memory) are done before 62662306a36Sopenharmony_ci * committing them to HW 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci wmb(); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci wil_w(wil, v->hwtail, v->swtail); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return rc; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci/** 63662306a36Sopenharmony_ci * reverse_memcmp - Compare two areas of memory, in reverse order 63762306a36Sopenharmony_ci * @cs: One area of memory 63862306a36Sopenharmony_ci * @ct: Another area of memory 63962306a36Sopenharmony_ci * @count: The size of the area. 64062306a36Sopenharmony_ci * 64162306a36Sopenharmony_ci * Cut'n'paste from original memcmp (see lib/string.c) 64262306a36Sopenharmony_ci * with minimal modifications 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_ciint reverse_memcmp(const void *cs, const void *ct, size_t count) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci const unsigned char *su1, *su2; 64762306a36Sopenharmony_ci int res = 0; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci for (su1 = cs + count - 1, su2 = ct + count - 1; count > 0; 65062306a36Sopenharmony_ci --su1, --su2, count--) { 65162306a36Sopenharmony_ci res = *su1 - *su2; 65262306a36Sopenharmony_ci if (res) 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci return res; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int wil_rx_crypto_check(struct wil6210_priv *wil, struct sk_buff *skb) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct vring_rx_desc *d = wil_skb_rxdesc(skb); 66162306a36Sopenharmony_ci int cid = wil_skb_get_cid(skb); 66262306a36Sopenharmony_ci int tid = wil_rxdesc_tid(d); 66362306a36Sopenharmony_ci int key_id = wil_rxdesc_key_id(d); 66462306a36Sopenharmony_ci int mc = wil_rxdesc_mcast(d); 66562306a36Sopenharmony_ci struct wil_sta_info *s = &wil->sta[cid]; 66662306a36Sopenharmony_ci struct wil_tid_crypto_rx *c = mc ? &s->group_crypto_rx : 66762306a36Sopenharmony_ci &s->tid_crypto_rx[tid]; 66862306a36Sopenharmony_ci struct wil_tid_crypto_rx_single *cc = &c->key_id[key_id]; 66962306a36Sopenharmony_ci const u8 *pn = (u8 *)&d->mac.pn; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (!cc->key_set) { 67262306a36Sopenharmony_ci wil_err_ratelimited(wil, 67362306a36Sopenharmony_ci "Key missing. CID %d TID %d MCast %d KEY_ID %d\n", 67462306a36Sopenharmony_ci cid, tid, mc, key_id); 67562306a36Sopenharmony_ci return -EINVAL; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (reverse_memcmp(pn, cc->pn, IEEE80211_GCMP_PN_LEN) <= 0) { 67962306a36Sopenharmony_ci wil_err_ratelimited(wil, 68062306a36Sopenharmony_ci "Replay attack. CID %d TID %d MCast %d KEY_ID %d PN %6phN last %6phN\n", 68162306a36Sopenharmony_ci cid, tid, mc, key_id, pn, cc->pn); 68262306a36Sopenharmony_ci return -EINVAL; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci memcpy(cc->pn, pn, IEEE80211_GCMP_PN_LEN); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic int wil_rx_error_check(struct wil6210_priv *wil, struct sk_buff *skb, 69062306a36Sopenharmony_ci struct wil_net_stats *stats) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct vring_rx_desc *d = wil_skb_rxdesc(skb); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if ((d->dma.status & RX_DMA_STATUS_ERROR) && 69562306a36Sopenharmony_ci (d->dma.error & RX_DMA_ERROR_MIC)) { 69662306a36Sopenharmony_ci stats->rx_mic_error++; 69762306a36Sopenharmony_ci wil_dbg_txrx(wil, "MIC error, dropping packet\n"); 69862306a36Sopenharmony_ci return -EFAULT; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic void wil_get_netif_rx_params(struct sk_buff *skb, int *cid, 70562306a36Sopenharmony_ci int *security) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct vring_rx_desc *d = wil_skb_rxdesc(skb); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci *cid = wil_skb_get_cid(skb); 71062306a36Sopenharmony_ci *security = wil_rxdesc_security(d); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/* 71462306a36Sopenharmony_ci * Check if skb is ptk eapol key message 71562306a36Sopenharmony_ci * 71662306a36Sopenharmony_ci * returns a pointer to the start of the eapol key structure, NULL 71762306a36Sopenharmony_ci * if frame is not PTK eapol key 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_cistatic struct wil_eapol_key *wil_is_ptk_eapol_key(struct wil6210_priv *wil, 72062306a36Sopenharmony_ci struct sk_buff *skb) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci u8 *buf; 72362306a36Sopenharmony_ci const struct wil_1x_hdr *hdr; 72462306a36Sopenharmony_ci struct wil_eapol_key *key; 72562306a36Sopenharmony_ci u16 key_info; 72662306a36Sopenharmony_ci int len = skb->len; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (!skb_mac_header_was_set(skb)) { 72962306a36Sopenharmony_ci wil_err(wil, "mac header was not set\n"); 73062306a36Sopenharmony_ci return NULL; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci len -= skb_mac_offset(skb); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (len < sizeof(struct ethhdr) + sizeof(struct wil_1x_hdr) + 73662306a36Sopenharmony_ci sizeof(struct wil_eapol_key)) 73762306a36Sopenharmony_ci return NULL; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci buf = skb_mac_header(skb) + sizeof(struct ethhdr); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci hdr = (const struct wil_1x_hdr *)buf; 74262306a36Sopenharmony_ci if (hdr->type != WIL_1X_TYPE_EAPOL_KEY) 74362306a36Sopenharmony_ci return NULL; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci key = (struct wil_eapol_key *)(buf + sizeof(struct wil_1x_hdr)); 74662306a36Sopenharmony_ci if (key->type != WIL_EAPOL_KEY_TYPE_WPA && 74762306a36Sopenharmony_ci key->type != WIL_EAPOL_KEY_TYPE_RSN) 74862306a36Sopenharmony_ci return NULL; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci key_info = be16_to_cpu(key->key_info); 75162306a36Sopenharmony_ci if (!(key_info & WIL_KEY_INFO_KEY_TYPE)) /* check if pairwise */ 75262306a36Sopenharmony_ci return NULL; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return key; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic bool wil_skb_is_eap_3(struct wil6210_priv *wil, struct sk_buff *skb) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct wil_eapol_key *key; 76062306a36Sopenharmony_ci u16 key_info; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci key = wil_is_ptk_eapol_key(wil, skb); 76362306a36Sopenharmony_ci if (!key) 76462306a36Sopenharmony_ci return false; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci key_info = be16_to_cpu(key->key_info); 76762306a36Sopenharmony_ci if (key_info & (WIL_KEY_INFO_MIC | 76862306a36Sopenharmony_ci WIL_KEY_INFO_ENCR_KEY_DATA)) { 76962306a36Sopenharmony_ci /* 3/4 of 4-Way Handshake */ 77062306a36Sopenharmony_ci wil_dbg_misc(wil, "EAPOL key message 3\n"); 77162306a36Sopenharmony_ci return true; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci /* 1/4 of 4-Way Handshake */ 77462306a36Sopenharmony_ci wil_dbg_misc(wil, "EAPOL key message 1\n"); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return false; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic bool wil_skb_is_eap_4(struct wil6210_priv *wil, struct sk_buff *skb) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci struct wil_eapol_key *key; 78262306a36Sopenharmony_ci u32 *nonce, i; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci key = wil_is_ptk_eapol_key(wil, skb); 78562306a36Sopenharmony_ci if (!key) 78662306a36Sopenharmony_ci return false; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci nonce = (u32 *)key->key_nonce; 78962306a36Sopenharmony_ci for (i = 0; i < WIL_EAP_NONCE_LEN / sizeof(u32); i++, nonce++) { 79062306a36Sopenharmony_ci if (*nonce != 0) { 79162306a36Sopenharmony_ci /* message 2/4 */ 79262306a36Sopenharmony_ci wil_dbg_misc(wil, "EAPOL key message 2\n"); 79362306a36Sopenharmony_ci return false; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci wil_dbg_misc(wil, "EAPOL key message 4\n"); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return true; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_civoid wil_enable_tx_key_worker(struct work_struct *work) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct wil6210_vif *vif = container_of(work, 80462306a36Sopenharmony_ci struct wil6210_vif, enable_tx_key_worker); 80562306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 80662306a36Sopenharmony_ci int rc, cid; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci rtnl_lock(); 80962306a36Sopenharmony_ci if (vif->ptk_rekey_state != WIL_REKEY_WAIT_M4_SENT) { 81062306a36Sopenharmony_ci wil_dbg_misc(wil, "Invalid rekey state = %d\n", 81162306a36Sopenharmony_ci vif->ptk_rekey_state); 81262306a36Sopenharmony_ci rtnl_unlock(); 81362306a36Sopenharmony_ci return; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci cid = wil_find_cid_by_idx(wil, vif->mid, 0); 81762306a36Sopenharmony_ci if (!wil_cid_valid(wil, cid)) { 81862306a36Sopenharmony_ci wil_err(wil, "Invalid cid = %d\n", cid); 81962306a36Sopenharmony_ci rtnl_unlock(); 82062306a36Sopenharmony_ci return; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci wil_dbg_misc(wil, "Apply PTK key after eapol was sent out\n"); 82462306a36Sopenharmony_ci rc = wmi_add_cipher_key(vif, 0, wil->sta[cid].addr, 0, NULL, 82562306a36Sopenharmony_ci WMI_KEY_USE_APPLY_PTK); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci vif->ptk_rekey_state = WIL_REKEY_IDLE; 82862306a36Sopenharmony_ci rtnl_unlock(); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (rc) 83162306a36Sopenharmony_ci wil_err(wil, "Apply PTK key failed %d\n", rc); 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_civoid wil_tx_complete_handle_eapol(struct wil6210_vif *vif, struct sk_buff *skb) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 83762306a36Sopenharmony_ci struct wireless_dev *wdev = vif_to_wdev(vif); 83862306a36Sopenharmony_ci bool q = false; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (wdev->iftype != NL80211_IFTYPE_STATION || 84162306a36Sopenharmony_ci !test_bit(WMI_FW_CAPABILITY_SPLIT_REKEY, wil->fw_capabilities)) 84262306a36Sopenharmony_ci return; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* check if skb is an EAP message 4/4 */ 84562306a36Sopenharmony_ci if (!wil_skb_is_eap_4(wil, skb)) 84662306a36Sopenharmony_ci return; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci spin_lock_bh(&wil->eap_lock); 84962306a36Sopenharmony_ci switch (vif->ptk_rekey_state) { 85062306a36Sopenharmony_ci case WIL_REKEY_IDLE: 85162306a36Sopenharmony_ci /* ignore idle state, can happen due to M4 retransmission */ 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci case WIL_REKEY_M3_RECEIVED: 85462306a36Sopenharmony_ci vif->ptk_rekey_state = WIL_REKEY_IDLE; 85562306a36Sopenharmony_ci break; 85662306a36Sopenharmony_ci case WIL_REKEY_WAIT_M4_SENT: 85762306a36Sopenharmony_ci q = true; 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci default: 86062306a36Sopenharmony_ci wil_err(wil, "Unknown rekey state = %d", 86162306a36Sopenharmony_ci vif->ptk_rekey_state); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci spin_unlock_bh(&wil->eap_lock); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (q) { 86662306a36Sopenharmony_ci q = queue_work(wil->wmi_wq, &vif->enable_tx_key_worker); 86762306a36Sopenharmony_ci wil_dbg_misc(wil, "queue_work of enable_tx_key_worker -> %d\n", 86862306a36Sopenharmony_ci q); 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic void wil_rx_handle_eapol(struct wil6210_vif *vif, struct sk_buff *skb) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 87562306a36Sopenharmony_ci struct wireless_dev *wdev = vif_to_wdev(vif); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (wdev->iftype != NL80211_IFTYPE_STATION || 87862306a36Sopenharmony_ci !test_bit(WMI_FW_CAPABILITY_SPLIT_REKEY, wil->fw_capabilities)) 87962306a36Sopenharmony_ci return; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* check if skb is a EAP message 3/4 */ 88262306a36Sopenharmony_ci if (!wil_skb_is_eap_3(wil, skb)) 88362306a36Sopenharmony_ci return; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (vif->ptk_rekey_state == WIL_REKEY_IDLE) 88662306a36Sopenharmony_ci vif->ptk_rekey_state = WIL_REKEY_M3_RECEIVED; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci/* 89062306a36Sopenharmony_ci * Pass Rx packet to the netif. Update statistics. 89162306a36Sopenharmony_ci * Called in softirq context (NAPI poll). 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_civoid wil_netif_rx(struct sk_buff *skb, struct net_device *ndev, int cid, 89462306a36Sopenharmony_ci struct wil_net_stats *stats, bool gro) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(ndev); 89762306a36Sopenharmony_ci struct wil6210_priv *wil = ndev_to_wil(ndev); 89862306a36Sopenharmony_ci struct wireless_dev *wdev = vif_to_wdev(vif); 89962306a36Sopenharmony_ci unsigned int len = skb->len; 90062306a36Sopenharmony_ci u8 *sa, *da = wil_skb_get_da(skb); 90162306a36Sopenharmony_ci /* here looking for DA, not A1, thus Rxdesc's 'mcast' indication 90262306a36Sopenharmony_ci * is not suitable, need to look at data 90362306a36Sopenharmony_ci */ 90462306a36Sopenharmony_ci int mcast = is_multicast_ether_addr(da); 90562306a36Sopenharmony_ci struct sk_buff *xmit_skb = NULL; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (wdev->iftype == NL80211_IFTYPE_STATION) { 90862306a36Sopenharmony_ci sa = wil_skb_get_sa(skb); 90962306a36Sopenharmony_ci if (mcast && ether_addr_equal(sa, ndev->dev_addr)) { 91062306a36Sopenharmony_ci /* mcast packet looped back to us */ 91162306a36Sopenharmony_ci dev_kfree_skb(skb); 91262306a36Sopenharmony_ci ndev->stats.rx_dropped++; 91362306a36Sopenharmony_ci stats->rx_dropped++; 91462306a36Sopenharmony_ci wil_dbg_txrx(wil, "Rx drop %d bytes\n", len); 91562306a36Sopenharmony_ci return; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci } else if (wdev->iftype == NL80211_IFTYPE_AP && !vif->ap_isolate) { 91862306a36Sopenharmony_ci if (mcast) { 91962306a36Sopenharmony_ci /* send multicast frames both to higher layers in 92062306a36Sopenharmony_ci * local net stack and back to the wireless medium 92162306a36Sopenharmony_ci */ 92262306a36Sopenharmony_ci xmit_skb = skb_copy(skb, GFP_ATOMIC); 92362306a36Sopenharmony_ci } else { 92462306a36Sopenharmony_ci int xmit_cid = wil_find_cid(wil, vif->mid, da); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (xmit_cid >= 0) { 92762306a36Sopenharmony_ci /* The destination station is associated to 92862306a36Sopenharmony_ci * this AP (in this VLAN), so send the frame 92962306a36Sopenharmony_ci * directly to it and do not pass it to local 93062306a36Sopenharmony_ci * net stack. 93162306a36Sopenharmony_ci */ 93262306a36Sopenharmony_ci xmit_skb = skb; 93362306a36Sopenharmony_ci skb = NULL; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci if (xmit_skb) { 93862306a36Sopenharmony_ci /* Send to wireless media and increase priority by 256 to 93962306a36Sopenharmony_ci * keep the received priority instead of reclassifying 94062306a36Sopenharmony_ci * the frame (see cfg80211_classify8021d). 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_ci xmit_skb->dev = ndev; 94362306a36Sopenharmony_ci xmit_skb->priority += 256; 94462306a36Sopenharmony_ci xmit_skb->protocol = htons(ETH_P_802_3); 94562306a36Sopenharmony_ci skb_reset_network_header(xmit_skb); 94662306a36Sopenharmony_ci skb_reset_mac_header(xmit_skb); 94762306a36Sopenharmony_ci wil_dbg_txrx(wil, "Rx -> Tx %d bytes\n", len); 94862306a36Sopenharmony_ci dev_queue_xmit(xmit_skb); 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (skb) { /* deliver to local stack */ 95262306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 95362306a36Sopenharmony_ci skb->dev = ndev; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (skb->protocol == cpu_to_be16(ETH_P_PAE)) 95662306a36Sopenharmony_ci wil_rx_handle_eapol(vif, skb); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (gro) 95962306a36Sopenharmony_ci napi_gro_receive(&wil->napi_rx, skb); 96062306a36Sopenharmony_ci else 96162306a36Sopenharmony_ci netif_rx(skb); 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci ndev->stats.rx_packets++; 96462306a36Sopenharmony_ci stats->rx_packets++; 96562306a36Sopenharmony_ci ndev->stats.rx_bytes += len; 96662306a36Sopenharmony_ci stats->rx_bytes += len; 96762306a36Sopenharmony_ci if (mcast) 96862306a36Sopenharmony_ci ndev->stats.multicast++; 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_civoid wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci int cid, security; 97462306a36Sopenharmony_ci struct wil6210_priv *wil = ndev_to_wil(ndev); 97562306a36Sopenharmony_ci struct wil_net_stats *stats; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci wil->txrx_ops.get_netif_rx_params(skb, &cid, &security); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci stats = &wil->sta[cid].stats; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci skb_orphan(skb); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (security && (wil->txrx_ops.rx_crypto_check(wil, skb) != 0)) { 98462306a36Sopenharmony_ci wil_dbg_txrx(wil, "Rx drop %d bytes\n", skb->len); 98562306a36Sopenharmony_ci dev_kfree_skb(skb); 98662306a36Sopenharmony_ci ndev->stats.rx_dropped++; 98762306a36Sopenharmony_ci stats->rx_replay++; 98862306a36Sopenharmony_ci stats->rx_dropped++; 98962306a36Sopenharmony_ci return; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* check errors reported by HW and update statistics */ 99362306a36Sopenharmony_ci if (unlikely(wil->txrx_ops.rx_error_check(wil, skb, stats))) { 99462306a36Sopenharmony_ci dev_kfree_skb(skb); 99562306a36Sopenharmony_ci return; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci wil_netif_rx(skb, ndev, cid, stats, true); 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci/* Proceed all completed skb's from Rx VRING 100262306a36Sopenharmony_ci * 100362306a36Sopenharmony_ci * Safe to call from NAPI poll, i.e. softirq with interrupts enabled 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_civoid wil_rx_handle(struct wil6210_priv *wil, int *quota) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct net_device *ndev = wil->main_ndev; 100862306a36Sopenharmony_ci struct wireless_dev *wdev = ndev->ieee80211_ptr; 100962306a36Sopenharmony_ci struct wil_ring *v = &wil->ring_rx; 101062306a36Sopenharmony_ci struct sk_buff *skb; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (unlikely(!v->va)) { 101362306a36Sopenharmony_ci wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); 101462306a36Sopenharmony_ci return; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci wil_dbg_txrx(wil, "rx_handle\n"); 101762306a36Sopenharmony_ci while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) { 101862306a36Sopenharmony_ci (*quota)--; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* monitor is currently supported on main interface only */ 102162306a36Sopenharmony_ci if (wdev->iftype == NL80211_IFTYPE_MONITOR) { 102262306a36Sopenharmony_ci skb->dev = ndev; 102362306a36Sopenharmony_ci skb_reset_mac_header(skb); 102462306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 102562306a36Sopenharmony_ci skb->pkt_type = PACKET_OTHERHOST; 102662306a36Sopenharmony_ci skb->protocol = htons(ETH_P_802_2); 102762306a36Sopenharmony_ci wil_netif_rx_any(skb, ndev); 102862306a36Sopenharmony_ci } else { 102962306a36Sopenharmony_ci wil_rx_reorder(wil, skb); 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci wil_rx_refill(wil, v->size); 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic void wil_rx_buf_len_init(struct wil6210_priv *wil) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci wil->rx_buf_len = rx_large_buf ? 103862306a36Sopenharmony_ci WIL_MAX_ETH_MTU : TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD; 103962306a36Sopenharmony_ci if (mtu_max > wil->rx_buf_len) { 104062306a36Sopenharmony_ci /* do not allow RX buffers to be smaller than mtu_max, for 104162306a36Sopenharmony_ci * backward compatibility (mtu_max parameter was also used 104262306a36Sopenharmony_ci * to support receiving large packets) 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_ci wil_info(wil, "Override RX buffer to mtu_max(%d)\n", mtu_max); 104562306a36Sopenharmony_ci wil->rx_buf_len = mtu_max; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic int wil_rx_init(struct wil6210_priv *wil, uint order) 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci struct wil_ring *vring = &wil->ring_rx; 105262306a36Sopenharmony_ci int rc; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci wil_dbg_misc(wil, "rx_init\n"); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (vring->va) { 105762306a36Sopenharmony_ci wil_err(wil, "Rx ring already allocated\n"); 105862306a36Sopenharmony_ci return -EINVAL; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci wil_rx_buf_len_init(wil); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci vring->size = 1 << order; 106462306a36Sopenharmony_ci vring->is_rx = true; 106562306a36Sopenharmony_ci rc = wil_vring_alloc(wil, vring); 106662306a36Sopenharmony_ci if (rc) 106762306a36Sopenharmony_ci return rc; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci rc = wmi_rx_chain_add(wil, vring); 107062306a36Sopenharmony_ci if (rc) 107162306a36Sopenharmony_ci goto err_free; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci rc = wil_rx_refill(wil, vring->size); 107462306a36Sopenharmony_ci if (rc) 107562306a36Sopenharmony_ci goto err_free; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci return 0; 107862306a36Sopenharmony_ci err_free: 107962306a36Sopenharmony_ci wil_vring_free(wil, vring); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci return rc; 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic void wil_rx_fini(struct wil6210_priv *wil) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct wil_ring *vring = &wil->ring_rx; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci wil_dbg_misc(wil, "rx_fini\n"); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (vring->va) 109162306a36Sopenharmony_ci wil_vring_free(wil, vring); 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic int wil_tx_desc_map(union wil_tx_desc *desc, dma_addr_t pa, 109562306a36Sopenharmony_ci u32 len, int vring_index) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci struct vring_tx_desc *d = &desc->legacy; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci wil_desc_addr_set(&d->dma.addr, pa); 110062306a36Sopenharmony_ci d->dma.ip_length = 0; 110162306a36Sopenharmony_ci /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ 110262306a36Sopenharmony_ci d->dma.b11 = 0/*14 | BIT(7)*/; 110362306a36Sopenharmony_ci d->dma.error = 0; 110462306a36Sopenharmony_ci d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ 110562306a36Sopenharmony_ci d->dma.length = cpu_to_le16((u16)len); 110662306a36Sopenharmony_ci d->dma.d0 = (vring_index << DMA_CFG_DESC_TX_0_QID_POS); 110762306a36Sopenharmony_ci d->mac.d[0] = 0; 110862306a36Sopenharmony_ci d->mac.d[1] = 0; 110962306a36Sopenharmony_ci d->mac.d[2] = 0; 111062306a36Sopenharmony_ci d->mac.ucode_cmd = 0; 111162306a36Sopenharmony_ci /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ 111262306a36Sopenharmony_ci d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | 111362306a36Sopenharmony_ci (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci return 0; 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_civoid wil_tx_data_init(struct wil_ring_tx_data *txdata) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci spin_lock_bh(&txdata->lock); 112162306a36Sopenharmony_ci txdata->dot1x_open = false; 112262306a36Sopenharmony_ci txdata->enabled = 0; 112362306a36Sopenharmony_ci txdata->idle = 0; 112462306a36Sopenharmony_ci txdata->last_idle = 0; 112562306a36Sopenharmony_ci txdata->begin = 0; 112662306a36Sopenharmony_ci txdata->agg_wsize = 0; 112762306a36Sopenharmony_ci txdata->agg_timeout = 0; 112862306a36Sopenharmony_ci txdata->agg_amsdu = 0; 112962306a36Sopenharmony_ci txdata->addba_in_progress = false; 113062306a36Sopenharmony_ci txdata->mid = U8_MAX; 113162306a36Sopenharmony_ci spin_unlock_bh(&txdata->lock); 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, 113562306a36Sopenharmony_ci int cid, int tid) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 113862306a36Sopenharmony_ci int rc; 113962306a36Sopenharmony_ci struct wmi_vring_cfg_cmd cmd = { 114062306a36Sopenharmony_ci .action = cpu_to_le32(WMI_VRING_CMD_ADD), 114162306a36Sopenharmony_ci .vring_cfg = { 114262306a36Sopenharmony_ci .tx_sw_ring = { 114362306a36Sopenharmony_ci .max_mpdu_size = 114462306a36Sopenharmony_ci cpu_to_le16(wil_mtu2macbuf(mtu_max)), 114562306a36Sopenharmony_ci .ring_size = cpu_to_le16(size), 114662306a36Sopenharmony_ci }, 114762306a36Sopenharmony_ci .ringid = id, 114862306a36Sopenharmony_ci .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, 114962306a36Sopenharmony_ci .mac_ctrl = 0, 115062306a36Sopenharmony_ci .to_resolution = 0, 115162306a36Sopenharmony_ci .agg_max_wsize = 0, 115262306a36Sopenharmony_ci .schd_params = { 115362306a36Sopenharmony_ci .priority = cpu_to_le16(0), 115462306a36Sopenharmony_ci .timeslot_us = cpu_to_le16(0xfff), 115562306a36Sopenharmony_ci }, 115662306a36Sopenharmony_ci }, 115762306a36Sopenharmony_ci }; 115862306a36Sopenharmony_ci struct { 115962306a36Sopenharmony_ci struct wmi_cmd_hdr wmi; 116062306a36Sopenharmony_ci struct wmi_vring_cfg_done_event cmd; 116162306a36Sopenharmony_ci } __packed reply = { 116262306a36Sopenharmony_ci .cmd = {.status = WMI_FW_STATUS_FAILURE}, 116362306a36Sopenharmony_ci }; 116462306a36Sopenharmony_ci struct wil_ring *vring = &wil->ring_tx[id]; 116562306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[id]; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (cid >= WIL6210_RX_DESC_MAX_CID) { 116862306a36Sopenharmony_ci cmd.vring_cfg.cidxtid = CIDXTID_EXTENDED_CID_TID; 116962306a36Sopenharmony_ci cmd.vring_cfg.cid = cid; 117062306a36Sopenharmony_ci cmd.vring_cfg.tid = tid; 117162306a36Sopenharmony_ci } else { 117262306a36Sopenharmony_ci cmd.vring_cfg.cidxtid = mk_cidxtid(cid, tid); 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci wil_dbg_misc(wil, "vring_init_tx: max_mpdu_size %d\n", 117662306a36Sopenharmony_ci cmd.vring_cfg.tx_sw_ring.max_mpdu_size); 117762306a36Sopenharmony_ci lockdep_assert_held(&wil->mutex); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (vring->va) { 118062306a36Sopenharmony_ci wil_err(wil, "Tx ring [%d] already allocated\n", id); 118162306a36Sopenharmony_ci rc = -EINVAL; 118262306a36Sopenharmony_ci goto out; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci wil_tx_data_init(txdata); 118662306a36Sopenharmony_ci vring->is_rx = false; 118762306a36Sopenharmony_ci vring->size = size; 118862306a36Sopenharmony_ci rc = wil_vring_alloc(wil, vring); 118962306a36Sopenharmony_ci if (rc) 119062306a36Sopenharmony_ci goto out; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci wil->ring2cid_tid[id][0] = cid; 119362306a36Sopenharmony_ci wil->ring2cid_tid[id][1] = tid; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (!vif->privacy) 119862306a36Sopenharmony_ci txdata->dot1x_open = true; 119962306a36Sopenharmony_ci rc = wmi_call(wil, WMI_VRING_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), 120062306a36Sopenharmony_ci WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 120162306a36Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 120262306a36Sopenharmony_ci if (rc) 120362306a36Sopenharmony_ci goto out_free; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) { 120662306a36Sopenharmony_ci wil_err(wil, "Tx config failed, status 0x%02x\n", 120762306a36Sopenharmony_ci reply.cmd.status); 120862306a36Sopenharmony_ci rc = -EINVAL; 120962306a36Sopenharmony_ci goto out_free; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci spin_lock_bh(&txdata->lock); 121362306a36Sopenharmony_ci vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); 121462306a36Sopenharmony_ci txdata->mid = vif->mid; 121562306a36Sopenharmony_ci txdata->enabled = 1; 121662306a36Sopenharmony_ci spin_unlock_bh(&txdata->lock); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (txdata->dot1x_open && (agg_wsize >= 0)) 121962306a36Sopenharmony_ci wil_addba_tx_request(wil, id, agg_wsize); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci return 0; 122262306a36Sopenharmony_ci out_free: 122362306a36Sopenharmony_ci spin_lock_bh(&txdata->lock); 122462306a36Sopenharmony_ci txdata->dot1x_open = false; 122562306a36Sopenharmony_ci txdata->enabled = 0; 122662306a36Sopenharmony_ci spin_unlock_bh(&txdata->lock); 122762306a36Sopenharmony_ci wil_vring_free(wil, vring); 122862306a36Sopenharmony_ci wil->ring2cid_tid[id][0] = wil->max_assoc_sta; 122962306a36Sopenharmony_ci wil->ring2cid_tid[id][1] = 0; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci out: 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci return rc; 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_cistatic int wil_tx_vring_modify(struct wil6210_vif *vif, int ring_id, int cid, 123762306a36Sopenharmony_ci int tid) 123862306a36Sopenharmony_ci{ 123962306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 124062306a36Sopenharmony_ci int rc; 124162306a36Sopenharmony_ci struct wmi_vring_cfg_cmd cmd = { 124262306a36Sopenharmony_ci .action = cpu_to_le32(WMI_VRING_CMD_MODIFY), 124362306a36Sopenharmony_ci .vring_cfg = { 124462306a36Sopenharmony_ci .tx_sw_ring = { 124562306a36Sopenharmony_ci .max_mpdu_size = 124662306a36Sopenharmony_ci cpu_to_le16(wil_mtu2macbuf(mtu_max)), 124762306a36Sopenharmony_ci .ring_size = 0, 124862306a36Sopenharmony_ci }, 124962306a36Sopenharmony_ci .ringid = ring_id, 125062306a36Sopenharmony_ci .cidxtid = mk_cidxtid(cid, tid), 125162306a36Sopenharmony_ci .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, 125262306a36Sopenharmony_ci .mac_ctrl = 0, 125362306a36Sopenharmony_ci .to_resolution = 0, 125462306a36Sopenharmony_ci .agg_max_wsize = 0, 125562306a36Sopenharmony_ci .schd_params = { 125662306a36Sopenharmony_ci .priority = cpu_to_le16(0), 125762306a36Sopenharmony_ci .timeslot_us = cpu_to_le16(0xfff), 125862306a36Sopenharmony_ci }, 125962306a36Sopenharmony_ci }, 126062306a36Sopenharmony_ci }; 126162306a36Sopenharmony_ci struct { 126262306a36Sopenharmony_ci struct wmi_cmd_hdr wmi; 126362306a36Sopenharmony_ci struct wmi_vring_cfg_done_event cmd; 126462306a36Sopenharmony_ci } __packed reply = { 126562306a36Sopenharmony_ci .cmd = {.status = WMI_FW_STATUS_FAILURE}, 126662306a36Sopenharmony_ci }; 126762306a36Sopenharmony_ci struct wil_ring *vring = &wil->ring_tx[ring_id]; 126862306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci wil_dbg_misc(wil, "vring_modify: ring %d cid %d tid %d\n", ring_id, 127162306a36Sopenharmony_ci cid, tid); 127262306a36Sopenharmony_ci lockdep_assert_held(&wil->mutex); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (!vring->va) { 127562306a36Sopenharmony_ci wil_err(wil, "Tx ring [%d] not allocated\n", ring_id); 127662306a36Sopenharmony_ci return -EINVAL; 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (wil->ring2cid_tid[ring_id][0] != cid || 128062306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][1] != tid) { 128162306a36Sopenharmony_ci wil_err(wil, "ring info does not match cid=%u tid=%u\n", 128262306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][0], 128362306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][1]); 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci rc = wmi_call(wil, WMI_VRING_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), 128962306a36Sopenharmony_ci WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 129062306a36Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 129162306a36Sopenharmony_ci if (rc) 129262306a36Sopenharmony_ci goto fail; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) { 129562306a36Sopenharmony_ci wil_err(wil, "Tx modify failed, status 0x%02x\n", 129662306a36Sopenharmony_ci reply.cmd.status); 129762306a36Sopenharmony_ci rc = -EINVAL; 129862306a36Sopenharmony_ci goto fail; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci /* set BA aggregation window size to 0 to force a new BA with the 130262306a36Sopenharmony_ci * new AP 130362306a36Sopenharmony_ci */ 130462306a36Sopenharmony_ci txdata->agg_wsize = 0; 130562306a36Sopenharmony_ci if (txdata->dot1x_open && agg_wsize >= 0) 130662306a36Sopenharmony_ci wil_addba_tx_request(wil, ring_id, agg_wsize); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci return 0; 130962306a36Sopenharmony_cifail: 131062306a36Sopenharmony_ci spin_lock_bh(&txdata->lock); 131162306a36Sopenharmony_ci txdata->dot1x_open = false; 131262306a36Sopenharmony_ci txdata->enabled = 0; 131362306a36Sopenharmony_ci spin_unlock_bh(&txdata->lock); 131462306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][0] = wil->max_assoc_sta; 131562306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][1] = 0; 131662306a36Sopenharmony_ci return rc; 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ciint wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 132262306a36Sopenharmony_ci int rc; 132362306a36Sopenharmony_ci struct wmi_bcast_vring_cfg_cmd cmd = { 132462306a36Sopenharmony_ci .action = cpu_to_le32(WMI_VRING_CMD_ADD), 132562306a36Sopenharmony_ci .vring_cfg = { 132662306a36Sopenharmony_ci .tx_sw_ring = { 132762306a36Sopenharmony_ci .max_mpdu_size = 132862306a36Sopenharmony_ci cpu_to_le16(wil_mtu2macbuf(mtu_max)), 132962306a36Sopenharmony_ci .ring_size = cpu_to_le16(size), 133062306a36Sopenharmony_ci }, 133162306a36Sopenharmony_ci .ringid = id, 133262306a36Sopenharmony_ci .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, 133362306a36Sopenharmony_ci }, 133462306a36Sopenharmony_ci }; 133562306a36Sopenharmony_ci struct { 133662306a36Sopenharmony_ci struct wmi_cmd_hdr wmi; 133762306a36Sopenharmony_ci struct wmi_vring_cfg_done_event cmd; 133862306a36Sopenharmony_ci } __packed reply = { 133962306a36Sopenharmony_ci .cmd = {.status = WMI_FW_STATUS_FAILURE}, 134062306a36Sopenharmony_ci }; 134162306a36Sopenharmony_ci struct wil_ring *vring = &wil->ring_tx[id]; 134262306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[id]; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci wil_dbg_misc(wil, "vring_init_bcast: max_mpdu_size %d\n", 134562306a36Sopenharmony_ci cmd.vring_cfg.tx_sw_ring.max_mpdu_size); 134662306a36Sopenharmony_ci lockdep_assert_held(&wil->mutex); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if (vring->va) { 134962306a36Sopenharmony_ci wil_err(wil, "Tx ring [%d] already allocated\n", id); 135062306a36Sopenharmony_ci rc = -EINVAL; 135162306a36Sopenharmony_ci goto out; 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci wil_tx_data_init(txdata); 135562306a36Sopenharmony_ci vring->is_rx = false; 135662306a36Sopenharmony_ci vring->size = size; 135762306a36Sopenharmony_ci rc = wil_vring_alloc(wil, vring); 135862306a36Sopenharmony_ci if (rc) 135962306a36Sopenharmony_ci goto out; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci wil->ring2cid_tid[id][0] = wil->max_assoc_sta; /* CID */ 136262306a36Sopenharmony_ci wil->ring2cid_tid[id][1] = 0; /* TID */ 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci if (!vif->privacy) 136762306a36Sopenharmony_ci txdata->dot1x_open = true; 136862306a36Sopenharmony_ci rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, vif->mid, 136962306a36Sopenharmony_ci &cmd, sizeof(cmd), 137062306a36Sopenharmony_ci WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 137162306a36Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 137262306a36Sopenharmony_ci if (rc) 137362306a36Sopenharmony_ci goto out_free; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) { 137662306a36Sopenharmony_ci wil_err(wil, "Tx config failed, status 0x%02x\n", 137762306a36Sopenharmony_ci reply.cmd.status); 137862306a36Sopenharmony_ci rc = -EINVAL; 137962306a36Sopenharmony_ci goto out_free; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci spin_lock_bh(&txdata->lock); 138362306a36Sopenharmony_ci vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); 138462306a36Sopenharmony_ci txdata->mid = vif->mid; 138562306a36Sopenharmony_ci txdata->enabled = 1; 138662306a36Sopenharmony_ci spin_unlock_bh(&txdata->lock); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci return 0; 138962306a36Sopenharmony_ci out_free: 139062306a36Sopenharmony_ci spin_lock_bh(&txdata->lock); 139162306a36Sopenharmony_ci txdata->enabled = 0; 139262306a36Sopenharmony_ci txdata->dot1x_open = false; 139362306a36Sopenharmony_ci spin_unlock_bh(&txdata->lock); 139462306a36Sopenharmony_ci wil_vring_free(wil, vring); 139562306a36Sopenharmony_ci out: 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci return rc; 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic struct wil_ring *wil_find_tx_ucast(struct wil6210_priv *wil, 140162306a36Sopenharmony_ci struct wil6210_vif *vif, 140262306a36Sopenharmony_ci struct sk_buff *skb) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci int i, cid; 140562306a36Sopenharmony_ci const u8 *da = wil_skb_get_da(skb); 140662306a36Sopenharmony_ci int min_ring_id = wil_get_min_tx_ring_id(wil); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci cid = wil_find_cid(wil, vif->mid, da); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci if (cid < 0 || cid >= wil->max_assoc_sta) 141162306a36Sopenharmony_ci return NULL; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci /* TODO: fix for multiple TID */ 141462306a36Sopenharmony_ci for (i = min_ring_id; i < ARRAY_SIZE(wil->ring2cid_tid); i++) { 141562306a36Sopenharmony_ci if (!wil->ring_tx_data[i].dot1x_open && 141662306a36Sopenharmony_ci skb->protocol != cpu_to_be16(ETH_P_PAE)) 141762306a36Sopenharmony_ci continue; 141862306a36Sopenharmony_ci if (wil->ring2cid_tid[i][0] == cid) { 141962306a36Sopenharmony_ci struct wil_ring *v = &wil->ring_tx[i]; 142062306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i]; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci wil_dbg_txrx(wil, "find_tx_ucast: (%pM) -> [%d]\n", 142362306a36Sopenharmony_ci da, i); 142462306a36Sopenharmony_ci if (v->va && txdata->enabled) { 142562306a36Sopenharmony_ci return v; 142662306a36Sopenharmony_ci } else { 142762306a36Sopenharmony_ci wil_dbg_txrx(wil, 142862306a36Sopenharmony_ci "find_tx_ucast: vring[%d] not valid\n", 142962306a36Sopenharmony_ci i); 143062306a36Sopenharmony_ci return NULL; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci return NULL; 143662306a36Sopenharmony_ci} 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic int wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, 143962306a36Sopenharmony_ci struct wil_ring *ring, struct sk_buff *skb); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cistatic struct wil_ring *wil_find_tx_ring_sta(struct wil6210_priv *wil, 144262306a36Sopenharmony_ci struct wil6210_vif *vif, 144362306a36Sopenharmony_ci struct sk_buff *skb) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct wil_ring *ring; 144662306a36Sopenharmony_ci int i; 144762306a36Sopenharmony_ci u8 cid; 144862306a36Sopenharmony_ci struct wil_ring_tx_data *txdata; 144962306a36Sopenharmony_ci int min_ring_id = wil_get_min_tx_ring_id(wil); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci /* In the STA mode, it is expected to have only 1 VRING 145262306a36Sopenharmony_ci * for the AP we connected to. 145362306a36Sopenharmony_ci * find 1-st vring eligible for this skb and use it. 145462306a36Sopenharmony_ci */ 145562306a36Sopenharmony_ci for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { 145662306a36Sopenharmony_ci ring = &wil->ring_tx[i]; 145762306a36Sopenharmony_ci txdata = &wil->ring_tx_data[i]; 145862306a36Sopenharmony_ci if (!ring->va || !txdata->enabled || txdata->mid != vif->mid) 145962306a36Sopenharmony_ci continue; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci cid = wil->ring2cid_tid[i][0]; 146262306a36Sopenharmony_ci if (cid >= wil->max_assoc_sta) /* skip BCAST */ 146362306a36Sopenharmony_ci continue; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (!wil->ring_tx_data[i].dot1x_open && 146662306a36Sopenharmony_ci skb->protocol != cpu_to_be16(ETH_P_PAE)) 146762306a36Sopenharmony_ci continue; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci wil_dbg_txrx(wil, "Tx -> ring %d\n", i); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci return ring; 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci wil_dbg_txrx(wil, "Tx while no rings active?\n"); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci return NULL; 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci/* Use one of 2 strategies: 148062306a36Sopenharmony_ci * 148162306a36Sopenharmony_ci * 1. New (real broadcast): 148262306a36Sopenharmony_ci * use dedicated broadcast vring 148362306a36Sopenharmony_ci * 2. Old (pseudo-DMS): 148462306a36Sopenharmony_ci * Find 1-st vring and return it; 148562306a36Sopenharmony_ci * duplicate skb and send it to other active vrings; 148662306a36Sopenharmony_ci * in all cases override dest address to unicast peer's address 148762306a36Sopenharmony_ci * Use old strategy when new is not supported yet: 148862306a36Sopenharmony_ci * - for PBSS 148962306a36Sopenharmony_ci */ 149062306a36Sopenharmony_cistatic struct wil_ring *wil_find_tx_bcast_1(struct wil6210_priv *wil, 149162306a36Sopenharmony_ci struct wil6210_vif *vif, 149262306a36Sopenharmony_ci struct sk_buff *skb) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci struct wil_ring *v; 149562306a36Sopenharmony_ci struct wil_ring_tx_data *txdata; 149662306a36Sopenharmony_ci int i = vif->bcast_ring; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci if (i < 0) 149962306a36Sopenharmony_ci return NULL; 150062306a36Sopenharmony_ci v = &wil->ring_tx[i]; 150162306a36Sopenharmony_ci txdata = &wil->ring_tx_data[i]; 150262306a36Sopenharmony_ci if (!v->va || !txdata->enabled) 150362306a36Sopenharmony_ci return NULL; 150462306a36Sopenharmony_ci if (!wil->ring_tx_data[i].dot1x_open && 150562306a36Sopenharmony_ci skb->protocol != cpu_to_be16(ETH_P_PAE)) 150662306a36Sopenharmony_ci return NULL; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci return v; 150962306a36Sopenharmony_ci} 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci/* apply multicast to unicast only for ARP and IP packets 151262306a36Sopenharmony_ci * (see NL80211_CMD_SET_MULTICAST_TO_UNICAST for more info) 151362306a36Sopenharmony_ci */ 151462306a36Sopenharmony_cistatic bool wil_check_multicast_to_unicast(struct wil6210_priv *wil, 151562306a36Sopenharmony_ci struct sk_buff *skb) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci const struct ethhdr *eth = (void *)skb->data; 151862306a36Sopenharmony_ci const struct vlan_ethhdr *ethvlan = (void *)skb->data; 151962306a36Sopenharmony_ci __be16 ethertype; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci if (!wil->multicast_to_unicast) 152262306a36Sopenharmony_ci return false; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* multicast to unicast conversion only for some payload */ 152562306a36Sopenharmony_ci ethertype = eth->h_proto; 152662306a36Sopenharmony_ci if (ethertype == htons(ETH_P_8021Q) && skb->len >= VLAN_ETH_HLEN) 152762306a36Sopenharmony_ci ethertype = ethvlan->h_vlan_encapsulated_proto; 152862306a36Sopenharmony_ci switch (ethertype) { 152962306a36Sopenharmony_ci case htons(ETH_P_ARP): 153062306a36Sopenharmony_ci case htons(ETH_P_IP): 153162306a36Sopenharmony_ci case htons(ETH_P_IPV6): 153262306a36Sopenharmony_ci break; 153362306a36Sopenharmony_ci default: 153462306a36Sopenharmony_ci return false; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci return true; 153862306a36Sopenharmony_ci} 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_cistatic void wil_set_da_for_vring(struct wil6210_priv *wil, 154162306a36Sopenharmony_ci struct sk_buff *skb, int vring_index) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci u8 *da = wil_skb_get_da(skb); 154462306a36Sopenharmony_ci int cid = wil->ring2cid_tid[vring_index][0]; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci ether_addr_copy(da, wil->sta[cid].addr); 154762306a36Sopenharmony_ci} 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_cistatic struct wil_ring *wil_find_tx_bcast_2(struct wil6210_priv *wil, 155062306a36Sopenharmony_ci struct wil6210_vif *vif, 155162306a36Sopenharmony_ci struct sk_buff *skb) 155262306a36Sopenharmony_ci{ 155362306a36Sopenharmony_ci struct wil_ring *v, *v2; 155462306a36Sopenharmony_ci struct sk_buff *skb2; 155562306a36Sopenharmony_ci int i; 155662306a36Sopenharmony_ci u8 cid; 155762306a36Sopenharmony_ci const u8 *src = wil_skb_get_sa(skb); 155862306a36Sopenharmony_ci struct wil_ring_tx_data *txdata, *txdata2; 155962306a36Sopenharmony_ci int min_ring_id = wil_get_min_tx_ring_id(wil); 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci /* find 1-st vring eligible for data */ 156262306a36Sopenharmony_ci for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { 156362306a36Sopenharmony_ci v = &wil->ring_tx[i]; 156462306a36Sopenharmony_ci txdata = &wil->ring_tx_data[i]; 156562306a36Sopenharmony_ci if (!v->va || !txdata->enabled || txdata->mid != vif->mid) 156662306a36Sopenharmony_ci continue; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci cid = wil->ring2cid_tid[i][0]; 156962306a36Sopenharmony_ci if (cid >= wil->max_assoc_sta) /* skip BCAST */ 157062306a36Sopenharmony_ci continue; 157162306a36Sopenharmony_ci if (!wil->ring_tx_data[i].dot1x_open && 157262306a36Sopenharmony_ci skb->protocol != cpu_to_be16(ETH_P_PAE)) 157362306a36Sopenharmony_ci continue; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci /* don't Tx back to source when re-routing Rx->Tx at the AP */ 157662306a36Sopenharmony_ci if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN)) 157762306a36Sopenharmony_ci continue; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci goto found; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci wil_dbg_txrx(wil, "Tx while no vrings active?\n"); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci return NULL; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_cifound: 158762306a36Sopenharmony_ci wil_dbg_txrx(wil, "BCAST -> ring %d\n", i); 158862306a36Sopenharmony_ci wil_set_da_for_vring(wil, skb, i); 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci /* find other active vrings and duplicate skb for each */ 159162306a36Sopenharmony_ci for (i++; i < WIL6210_MAX_TX_RINGS; i++) { 159262306a36Sopenharmony_ci v2 = &wil->ring_tx[i]; 159362306a36Sopenharmony_ci txdata2 = &wil->ring_tx_data[i]; 159462306a36Sopenharmony_ci if (!v2->va || txdata2->mid != vif->mid) 159562306a36Sopenharmony_ci continue; 159662306a36Sopenharmony_ci cid = wil->ring2cid_tid[i][0]; 159762306a36Sopenharmony_ci if (cid >= wil->max_assoc_sta) /* skip BCAST */ 159862306a36Sopenharmony_ci continue; 159962306a36Sopenharmony_ci if (!wil->ring_tx_data[i].dot1x_open && 160062306a36Sopenharmony_ci skb->protocol != cpu_to_be16(ETH_P_PAE)) 160162306a36Sopenharmony_ci continue; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN)) 160462306a36Sopenharmony_ci continue; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci skb2 = skb_copy(skb, GFP_ATOMIC); 160762306a36Sopenharmony_ci if (skb2) { 160862306a36Sopenharmony_ci wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i); 160962306a36Sopenharmony_ci wil_set_da_for_vring(wil, skb2, i); 161062306a36Sopenharmony_ci wil_tx_ring(wil, vif, v2, skb2); 161162306a36Sopenharmony_ci /* successful call to wil_tx_ring takes skb2 ref */ 161262306a36Sopenharmony_ci dev_kfree_skb_any(skb2); 161362306a36Sopenharmony_ci } else { 161462306a36Sopenharmony_ci wil_err(wil, "skb_copy failed\n"); 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci return v; 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_cistatic inline 162262306a36Sopenharmony_civoid wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags) 162362306a36Sopenharmony_ci{ 162462306a36Sopenharmony_ci d->mac.d[2] |= (nr_frags << MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); 162562306a36Sopenharmony_ci} 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci/* Sets the descriptor @d up for csum and/or TSO offloading. The corresponding 162862306a36Sopenharmony_ci * @skb is used to obtain the protocol and headers length. 162962306a36Sopenharmony_ci * @tso_desc_type is a descriptor type for TSO: 0 - a header, 1 - first data, 163062306a36Sopenharmony_ci * 2 - middle, 3 - last descriptor. 163162306a36Sopenharmony_ci */ 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_cistatic void wil_tx_desc_offload_setup_tso(struct vring_tx_desc *d, 163462306a36Sopenharmony_ci struct sk_buff *skb, 163562306a36Sopenharmony_ci int tso_desc_type, bool is_ipv4, 163662306a36Sopenharmony_ci int tcp_hdr_len, int skb_net_hdr_len) 163762306a36Sopenharmony_ci{ 163862306a36Sopenharmony_ci d->dma.b11 = ETH_HLEN; /* MAC header length */ 163962306a36Sopenharmony_ci d->dma.b11 |= is_ipv4 << DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci d->dma.d0 |= (2 << DMA_CFG_DESC_TX_0_L4_TYPE_POS); 164262306a36Sopenharmony_ci /* L4 header len: TCP header length */ 164362306a36Sopenharmony_ci d->dma.d0 |= (tcp_hdr_len & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci /* Setup TSO: bit and desc type */ 164662306a36Sopenharmony_ci d->dma.d0 |= (BIT(DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS)) | 164762306a36Sopenharmony_ci (tso_desc_type << DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS); 164862306a36Sopenharmony_ci d->dma.d0 |= (is_ipv4 << DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci d->dma.ip_length = skb_net_hdr_len; 165162306a36Sopenharmony_ci /* Enable TCP/UDP checksum */ 165262306a36Sopenharmony_ci d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS); 165362306a36Sopenharmony_ci /* Calculate pseudo-header */ 165462306a36Sopenharmony_ci d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS); 165562306a36Sopenharmony_ci} 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci/* Sets the descriptor @d up for csum. The corresponding 165862306a36Sopenharmony_ci * @skb is used to obtain the protocol and headers length. 165962306a36Sopenharmony_ci * Returns the protocol: 0 - not TCP, 1 - TCPv4, 2 - TCPv6. 166062306a36Sopenharmony_ci * Note, if d==NULL, the function only returns the protocol result. 166162306a36Sopenharmony_ci * 166262306a36Sopenharmony_ci * It is very similar to previous wil_tx_desc_offload_setup_tso. This 166362306a36Sopenharmony_ci * is "if unrolling" to optimize the critical path. 166462306a36Sopenharmony_ci */ 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_cistatic int wil_tx_desc_offload_setup(struct vring_tx_desc *d, 166762306a36Sopenharmony_ci struct sk_buff *skb){ 166862306a36Sopenharmony_ci int protocol; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 167162306a36Sopenharmony_ci return 0; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci d->dma.b11 = ETH_HLEN; /* MAC header length */ 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci switch (skb->protocol) { 167662306a36Sopenharmony_ci case cpu_to_be16(ETH_P_IP): 167762306a36Sopenharmony_ci protocol = ip_hdr(skb)->protocol; 167862306a36Sopenharmony_ci d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS); 167962306a36Sopenharmony_ci break; 168062306a36Sopenharmony_ci case cpu_to_be16(ETH_P_IPV6): 168162306a36Sopenharmony_ci protocol = ipv6_hdr(skb)->nexthdr; 168262306a36Sopenharmony_ci break; 168362306a36Sopenharmony_ci default: 168462306a36Sopenharmony_ci return -EINVAL; 168562306a36Sopenharmony_ci } 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci switch (protocol) { 168862306a36Sopenharmony_ci case IPPROTO_TCP: 168962306a36Sopenharmony_ci d->dma.d0 |= (2 << DMA_CFG_DESC_TX_0_L4_TYPE_POS); 169062306a36Sopenharmony_ci /* L4 header len: TCP header length */ 169162306a36Sopenharmony_ci d->dma.d0 |= 169262306a36Sopenharmony_ci (tcp_hdrlen(skb) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK); 169362306a36Sopenharmony_ci break; 169462306a36Sopenharmony_ci case IPPROTO_UDP: 169562306a36Sopenharmony_ci /* L4 header len: UDP header length */ 169662306a36Sopenharmony_ci d->dma.d0 |= 169762306a36Sopenharmony_ci (sizeof(struct udphdr) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK); 169862306a36Sopenharmony_ci break; 169962306a36Sopenharmony_ci default: 170062306a36Sopenharmony_ci return -EINVAL; 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci d->dma.ip_length = skb_network_header_len(skb); 170462306a36Sopenharmony_ci /* Enable TCP/UDP checksum */ 170562306a36Sopenharmony_ci d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS); 170662306a36Sopenharmony_ci /* Calculate pseudo-header */ 170762306a36Sopenharmony_ci d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci return 0; 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_cistatic inline void wil_tx_last_desc(struct vring_tx_desc *d) 171362306a36Sopenharmony_ci{ 171462306a36Sopenharmony_ci d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS) | 171562306a36Sopenharmony_ci BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS) | 171662306a36Sopenharmony_ci BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistatic inline void wil_set_tx_desc_last_tso(volatile struct vring_tx_desc *d) 172062306a36Sopenharmony_ci{ 172162306a36Sopenharmony_ci d->dma.d0 |= wil_tso_type_lst << 172262306a36Sopenharmony_ci DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS; 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cistatic int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif, 172662306a36Sopenharmony_ci struct wil_ring *vring, struct sk_buff *skb) 172762306a36Sopenharmony_ci{ 172862306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci /* point to descriptors in shared memory */ 173162306a36Sopenharmony_ci volatile struct vring_tx_desc *_desc = NULL, *_hdr_desc, 173262306a36Sopenharmony_ci *_first_desc = NULL; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci /* pointers to shadow descriptors */ 173562306a36Sopenharmony_ci struct vring_tx_desc desc_mem, hdr_desc_mem, first_desc_mem, 173662306a36Sopenharmony_ci *d = &hdr_desc_mem, *hdr_desc = &hdr_desc_mem, 173762306a36Sopenharmony_ci *first_desc = &first_desc_mem; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci /* pointer to shadow descriptors' context */ 174062306a36Sopenharmony_ci struct wil_ctx *hdr_ctx, *first_ctx = NULL; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci int descs_used = 0; /* total number of used descriptors */ 174362306a36Sopenharmony_ci int sg_desc_cnt = 0; /* number of descriptors for current mss*/ 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci u32 swhead = vring->swhead; 174662306a36Sopenharmony_ci int used, avail = wil_ring_avail_tx(vring); 174762306a36Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 174862306a36Sopenharmony_ci int min_desc_required = nr_frags + 1; 174962306a36Sopenharmony_ci int mss = skb_shinfo(skb)->gso_size; /* payload size w/o headers */ 175062306a36Sopenharmony_ci int f, len, hdrlen, headlen; 175162306a36Sopenharmony_ci int vring_index = vring - wil->ring_tx; 175262306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[vring_index]; 175362306a36Sopenharmony_ci uint i = swhead; 175462306a36Sopenharmony_ci dma_addr_t pa; 175562306a36Sopenharmony_ci const skb_frag_t *frag = NULL; 175662306a36Sopenharmony_ci int rem_data = mss; 175762306a36Sopenharmony_ci int lenmss; 175862306a36Sopenharmony_ci int hdr_compensation_need = true; 175962306a36Sopenharmony_ci int desc_tso_type = wil_tso_type_first; 176062306a36Sopenharmony_ci bool is_ipv4; 176162306a36Sopenharmony_ci int tcp_hdr_len; 176262306a36Sopenharmony_ci int skb_net_hdr_len; 176362306a36Sopenharmony_ci int gso_type; 176462306a36Sopenharmony_ci int rc = -EINVAL; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci wil_dbg_txrx(wil, "tx_vring_tso: %d bytes to vring %d\n", skb->len, 176762306a36Sopenharmony_ci vring_index); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if (unlikely(!txdata->enabled)) 177062306a36Sopenharmony_ci return -EINVAL; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci /* A typical page 4K is 3-4 payloads, we assume each fragment 177362306a36Sopenharmony_ci * is a full payload, that's how min_desc_required has been 177462306a36Sopenharmony_ci * calculated. In real we might need more or less descriptors, 177562306a36Sopenharmony_ci * this is the initial check only. 177662306a36Sopenharmony_ci */ 177762306a36Sopenharmony_ci if (unlikely(avail < min_desc_required)) { 177862306a36Sopenharmony_ci wil_err_ratelimited(wil, 177962306a36Sopenharmony_ci "TSO: Tx ring[%2d] full. No space for %d fragments\n", 178062306a36Sopenharmony_ci vring_index, min_desc_required); 178162306a36Sopenharmony_ci return -ENOMEM; 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci /* Header Length = MAC header len + IP header len + TCP header len*/ 178562306a36Sopenharmony_ci hdrlen = skb_tcp_all_headers(skb); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci gso_type = skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV6 | SKB_GSO_TCPV4); 178862306a36Sopenharmony_ci switch (gso_type) { 178962306a36Sopenharmony_ci case SKB_GSO_TCPV4: 179062306a36Sopenharmony_ci /* TCP v4, zero out the IP length and IPv4 checksum fields 179162306a36Sopenharmony_ci * as required by the offloading doc 179262306a36Sopenharmony_ci */ 179362306a36Sopenharmony_ci ip_hdr(skb)->tot_len = 0; 179462306a36Sopenharmony_ci ip_hdr(skb)->check = 0; 179562306a36Sopenharmony_ci is_ipv4 = true; 179662306a36Sopenharmony_ci break; 179762306a36Sopenharmony_ci case SKB_GSO_TCPV6: 179862306a36Sopenharmony_ci /* TCP v6, zero out the payload length */ 179962306a36Sopenharmony_ci ipv6_hdr(skb)->payload_len = 0; 180062306a36Sopenharmony_ci is_ipv4 = false; 180162306a36Sopenharmony_ci break; 180262306a36Sopenharmony_ci default: 180362306a36Sopenharmony_ci /* other than TCPv4 or TCPv6 types are not supported for TSO. 180462306a36Sopenharmony_ci * It is also illegal for both to be set simultaneously 180562306a36Sopenharmony_ci */ 180662306a36Sopenharmony_ci return -EINVAL; 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 181062306a36Sopenharmony_ci return -EINVAL; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci /* tcp header length and skb network header length are fixed for all 181362306a36Sopenharmony_ci * packet's descriptors - read then once here 181462306a36Sopenharmony_ci */ 181562306a36Sopenharmony_ci tcp_hdr_len = tcp_hdrlen(skb); 181662306a36Sopenharmony_ci skb_net_hdr_len = skb_network_header_len(skb); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci _hdr_desc = &vring->va[i].tx.legacy; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci pa = dma_map_single(dev, skb->data, hdrlen, DMA_TO_DEVICE); 182162306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, pa))) { 182262306a36Sopenharmony_ci wil_err(wil, "TSO: Skb head DMA map error\n"); 182362306a36Sopenharmony_ci goto err_exit; 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci wil->txrx_ops.tx_desc_map((union wil_tx_desc *)hdr_desc, pa, 182762306a36Sopenharmony_ci hdrlen, vring_index); 182862306a36Sopenharmony_ci wil_tx_desc_offload_setup_tso(hdr_desc, skb, wil_tso_type_hdr, is_ipv4, 182962306a36Sopenharmony_ci tcp_hdr_len, skb_net_hdr_len); 183062306a36Sopenharmony_ci wil_tx_last_desc(hdr_desc); 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci vring->ctx[i].mapped_as = wil_mapped_as_single; 183362306a36Sopenharmony_ci hdr_ctx = &vring->ctx[i]; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci descs_used++; 183662306a36Sopenharmony_ci headlen = skb_headlen(skb) - hdrlen; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci for (f = headlen ? -1 : 0; f < nr_frags; f++) { 183962306a36Sopenharmony_ci if (headlen) { 184062306a36Sopenharmony_ci len = headlen; 184162306a36Sopenharmony_ci wil_dbg_txrx(wil, "TSO: process skb head, len %u\n", 184262306a36Sopenharmony_ci len); 184362306a36Sopenharmony_ci } else { 184462306a36Sopenharmony_ci frag = &skb_shinfo(skb)->frags[f]; 184562306a36Sopenharmony_ci len = skb_frag_size(frag); 184662306a36Sopenharmony_ci wil_dbg_txrx(wil, "TSO: frag[%d]: len %u\n", f, len); 184762306a36Sopenharmony_ci } 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci while (len) { 185062306a36Sopenharmony_ci wil_dbg_txrx(wil, 185162306a36Sopenharmony_ci "TSO: len %d, rem_data %d, descs_used %d\n", 185262306a36Sopenharmony_ci len, rem_data, descs_used); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci if (descs_used == avail) { 185562306a36Sopenharmony_ci wil_err_ratelimited(wil, "TSO: ring overflow\n"); 185662306a36Sopenharmony_ci rc = -ENOMEM; 185762306a36Sopenharmony_ci goto mem_error; 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci lenmss = min_t(int, rem_data, len); 186162306a36Sopenharmony_ci i = (swhead + descs_used) % vring->size; 186262306a36Sopenharmony_ci wil_dbg_txrx(wil, "TSO: lenmss %d, i %d\n", lenmss, i); 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci if (!headlen) { 186562306a36Sopenharmony_ci pa = skb_frag_dma_map(dev, frag, 186662306a36Sopenharmony_ci skb_frag_size(frag) - len, 186762306a36Sopenharmony_ci lenmss, DMA_TO_DEVICE); 186862306a36Sopenharmony_ci vring->ctx[i].mapped_as = wil_mapped_as_page; 186962306a36Sopenharmony_ci } else { 187062306a36Sopenharmony_ci pa = dma_map_single(dev, 187162306a36Sopenharmony_ci skb->data + 187262306a36Sopenharmony_ci skb_headlen(skb) - headlen, 187362306a36Sopenharmony_ci lenmss, 187462306a36Sopenharmony_ci DMA_TO_DEVICE); 187562306a36Sopenharmony_ci vring->ctx[i].mapped_as = wil_mapped_as_single; 187662306a36Sopenharmony_ci headlen -= lenmss; 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, pa))) { 188062306a36Sopenharmony_ci wil_err(wil, "TSO: DMA map page error\n"); 188162306a36Sopenharmony_ci goto mem_error; 188262306a36Sopenharmony_ci } 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci _desc = &vring->va[i].tx.legacy; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci if (!_first_desc) { 188762306a36Sopenharmony_ci _first_desc = _desc; 188862306a36Sopenharmony_ci first_ctx = &vring->ctx[i]; 188962306a36Sopenharmony_ci d = first_desc; 189062306a36Sopenharmony_ci } else { 189162306a36Sopenharmony_ci d = &desc_mem; 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, 189562306a36Sopenharmony_ci pa, lenmss, vring_index); 189662306a36Sopenharmony_ci wil_tx_desc_offload_setup_tso(d, skb, desc_tso_type, 189762306a36Sopenharmony_ci is_ipv4, tcp_hdr_len, 189862306a36Sopenharmony_ci skb_net_hdr_len); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci /* use tso_type_first only once */ 190162306a36Sopenharmony_ci desc_tso_type = wil_tso_type_mid; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci descs_used++; /* desc used so far */ 190462306a36Sopenharmony_ci sg_desc_cnt++; /* desc used for this segment */ 190562306a36Sopenharmony_ci len -= lenmss; 190662306a36Sopenharmony_ci rem_data -= lenmss; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci wil_dbg_txrx(wil, 190962306a36Sopenharmony_ci "TSO: len %d, rem_data %d, descs_used %d, sg_desc_cnt %d,\n", 191062306a36Sopenharmony_ci len, rem_data, descs_used, sg_desc_cnt); 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci /* Close the segment if reached mss size or last frag*/ 191362306a36Sopenharmony_ci if (rem_data == 0 || (f == nr_frags - 1 && len == 0)) { 191462306a36Sopenharmony_ci if (hdr_compensation_need) { 191562306a36Sopenharmony_ci /* first segment include hdr desc for 191662306a36Sopenharmony_ci * release 191762306a36Sopenharmony_ci */ 191862306a36Sopenharmony_ci hdr_ctx->nr_frags = sg_desc_cnt; 191962306a36Sopenharmony_ci wil_tx_desc_set_nr_frags(first_desc, 192062306a36Sopenharmony_ci sg_desc_cnt + 192162306a36Sopenharmony_ci 1); 192262306a36Sopenharmony_ci hdr_compensation_need = false; 192362306a36Sopenharmony_ci } else { 192462306a36Sopenharmony_ci wil_tx_desc_set_nr_frags(first_desc, 192562306a36Sopenharmony_ci sg_desc_cnt); 192662306a36Sopenharmony_ci } 192762306a36Sopenharmony_ci first_ctx->nr_frags = sg_desc_cnt - 1; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci wil_tx_last_desc(d); 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci /* first descriptor may also be the last 193262306a36Sopenharmony_ci * for this mss - make sure not to copy 193362306a36Sopenharmony_ci * it twice 193462306a36Sopenharmony_ci */ 193562306a36Sopenharmony_ci if (first_desc != d) 193662306a36Sopenharmony_ci *_first_desc = *first_desc; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci /*last descriptor will be copied at the end 193962306a36Sopenharmony_ci * of this TS processing 194062306a36Sopenharmony_ci */ 194162306a36Sopenharmony_ci if (f < nr_frags - 1 || len > 0) 194262306a36Sopenharmony_ci *_desc = *d; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci rem_data = mss; 194562306a36Sopenharmony_ci _first_desc = NULL; 194662306a36Sopenharmony_ci sg_desc_cnt = 0; 194762306a36Sopenharmony_ci } else if (first_desc != d) /* update mid descriptor */ 194862306a36Sopenharmony_ci *_desc = *d; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci if (!_desc) 195362306a36Sopenharmony_ci goto mem_error; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci /* first descriptor may also be the last. 195662306a36Sopenharmony_ci * in this case d pointer is invalid 195762306a36Sopenharmony_ci */ 195862306a36Sopenharmony_ci if (_first_desc == _desc) 195962306a36Sopenharmony_ci d = first_desc; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci /* Last data descriptor */ 196262306a36Sopenharmony_ci wil_set_tx_desc_last_tso(d); 196362306a36Sopenharmony_ci *_desc = *d; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci /* Fill the total number of descriptors in first desc (hdr)*/ 196662306a36Sopenharmony_ci wil_tx_desc_set_nr_frags(hdr_desc, descs_used); 196762306a36Sopenharmony_ci *_hdr_desc = *hdr_desc; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci /* hold reference to skb 197062306a36Sopenharmony_ci * to prevent skb release before accounting 197162306a36Sopenharmony_ci * in case of immediate "tx done" 197262306a36Sopenharmony_ci */ 197362306a36Sopenharmony_ci vring->ctx[i].skb = skb_get(skb); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci /* performance monitoring */ 197662306a36Sopenharmony_ci used = wil_ring_used_tx(vring); 197762306a36Sopenharmony_ci if (wil_val_in_range(wil->ring_idle_trsh, 197862306a36Sopenharmony_ci used, used + descs_used)) { 197962306a36Sopenharmony_ci txdata->idle += get_cycles() - txdata->last_idle; 198062306a36Sopenharmony_ci wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n", 198162306a36Sopenharmony_ci vring_index, used, used + descs_used); 198262306a36Sopenharmony_ci } 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci /* Make sure to advance the head only after descriptor update is done. 198562306a36Sopenharmony_ci * This will prevent a race condition where the completion thread 198662306a36Sopenharmony_ci * will see the DU bit set from previous run and will handle the 198762306a36Sopenharmony_ci * skb before it was completed. 198862306a36Sopenharmony_ci */ 198962306a36Sopenharmony_ci wmb(); 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci /* advance swhead */ 199262306a36Sopenharmony_ci wil_ring_advance_head(vring, descs_used); 199362306a36Sopenharmony_ci wil_dbg_txrx(wil, "TSO: Tx swhead %d -> %d\n", swhead, vring->swhead); 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci /* make sure all writes to descriptors (shared memory) are done before 199662306a36Sopenharmony_ci * committing them to HW 199762306a36Sopenharmony_ci */ 199862306a36Sopenharmony_ci wmb(); 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci if (wil->tx_latency) 200162306a36Sopenharmony_ci *(ktime_t *)&skb->cb = ktime_get(); 200262306a36Sopenharmony_ci else 200362306a36Sopenharmony_ci memset(skb->cb, 0, sizeof(ktime_t)); 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci wil_w(wil, vring->hwtail, vring->swhead); 200662306a36Sopenharmony_ci return 0; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_cimem_error: 200962306a36Sopenharmony_ci while (descs_used > 0) { 201062306a36Sopenharmony_ci struct wil_ctx *ctx; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci i = (swhead + descs_used - 1) % vring->size; 201362306a36Sopenharmony_ci d = (struct vring_tx_desc *)&vring->va[i].tx.legacy; 201462306a36Sopenharmony_ci _desc = &vring->va[i].tx.legacy; 201562306a36Sopenharmony_ci *d = *_desc; 201662306a36Sopenharmony_ci _desc->dma.status = TX_DMA_STATUS_DU; 201762306a36Sopenharmony_ci ctx = &vring->ctx[i]; 201862306a36Sopenharmony_ci wil_txdesc_unmap(dev, (union wil_tx_desc *)d, ctx); 201962306a36Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 202062306a36Sopenharmony_ci descs_used--; 202162306a36Sopenharmony_ci } 202262306a36Sopenharmony_cierr_exit: 202362306a36Sopenharmony_ci return rc; 202462306a36Sopenharmony_ci} 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_cistatic int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, 202762306a36Sopenharmony_ci struct wil_ring *ring, struct sk_buff *skb) 202862306a36Sopenharmony_ci{ 202962306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 203062306a36Sopenharmony_ci struct vring_tx_desc dd, *d = ⅆ 203162306a36Sopenharmony_ci volatile struct vring_tx_desc *_d; 203262306a36Sopenharmony_ci u32 swhead = ring->swhead; 203362306a36Sopenharmony_ci int avail = wil_ring_avail_tx(ring); 203462306a36Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 203562306a36Sopenharmony_ci uint f = 0; 203662306a36Sopenharmony_ci int ring_index = ring - wil->ring_tx; 203762306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_index]; 203862306a36Sopenharmony_ci uint i = swhead; 203962306a36Sopenharmony_ci dma_addr_t pa; 204062306a36Sopenharmony_ci int used; 204162306a36Sopenharmony_ci bool mcast = (ring_index == vif->bcast_ring); 204262306a36Sopenharmony_ci uint len = skb_headlen(skb); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci wil_dbg_txrx(wil, "tx_ring: %d bytes to ring %d, nr_frags %d\n", 204562306a36Sopenharmony_ci skb->len, ring_index, nr_frags); 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci if (unlikely(!txdata->enabled)) 204862306a36Sopenharmony_ci return -EINVAL; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci if (unlikely(avail < 1 + nr_frags)) { 205162306a36Sopenharmony_ci wil_err_ratelimited(wil, 205262306a36Sopenharmony_ci "Tx ring[%2d] full. No space for %d fragments\n", 205362306a36Sopenharmony_ci ring_index, 1 + nr_frags); 205462306a36Sopenharmony_ci return -ENOMEM; 205562306a36Sopenharmony_ci } 205662306a36Sopenharmony_ci _d = &ring->va[i].tx.legacy; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci wil_dbg_txrx(wil, "Tx[%2d] skb %d bytes 0x%p -> %pad\n", ring_index, 206162306a36Sopenharmony_ci skb_headlen(skb), skb->data, &pa); 206262306a36Sopenharmony_ci wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1, 206362306a36Sopenharmony_ci skb->data, skb_headlen(skb), false); 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, pa))) 206662306a36Sopenharmony_ci return -EINVAL; 206762306a36Sopenharmony_ci ring->ctx[i].mapped_as = wil_mapped_as_single; 206862306a36Sopenharmony_ci /* 1-st segment */ 206962306a36Sopenharmony_ci wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, pa, len, 207062306a36Sopenharmony_ci ring_index); 207162306a36Sopenharmony_ci if (unlikely(mcast)) { 207262306a36Sopenharmony_ci d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_0_MCS_EN_POS); /* MCS 0 */ 207362306a36Sopenharmony_ci if (unlikely(len > WIL_BCAST_MCS0_LIMIT)) /* set MCS 1 */ 207462306a36Sopenharmony_ci d->mac.d[0] |= (1 << MAC_CFG_DESC_TX_0_MCS_INDEX_POS); 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci /* Process TCP/UDP checksum offloading */ 207762306a36Sopenharmony_ci if (unlikely(wil_tx_desc_offload_setup(d, skb))) { 207862306a36Sopenharmony_ci wil_err(wil, "Tx[%2d] Failed to set cksum, drop packet\n", 207962306a36Sopenharmony_ci ring_index); 208062306a36Sopenharmony_ci goto dma_error; 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci ring->ctx[i].nr_frags = nr_frags; 208462306a36Sopenharmony_ci wil_tx_desc_set_nr_frags(d, nr_frags + 1); 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci /* middle segments */ 208762306a36Sopenharmony_ci for (; f < nr_frags; f++) { 208862306a36Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; 208962306a36Sopenharmony_ci int len = skb_frag_size(frag); 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci *_d = *d; 209262306a36Sopenharmony_ci wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", ring_index, i); 209362306a36Sopenharmony_ci wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4, 209462306a36Sopenharmony_ci (const void *)d, sizeof(*d), false); 209562306a36Sopenharmony_ci i = (swhead + f + 1) % ring->size; 209662306a36Sopenharmony_ci _d = &ring->va[i].tx.legacy; 209762306a36Sopenharmony_ci pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), 209862306a36Sopenharmony_ci DMA_TO_DEVICE); 209962306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, pa))) { 210062306a36Sopenharmony_ci wil_err(wil, "Tx[%2d] failed to map fragment\n", 210162306a36Sopenharmony_ci ring_index); 210262306a36Sopenharmony_ci goto dma_error; 210362306a36Sopenharmony_ci } 210462306a36Sopenharmony_ci ring->ctx[i].mapped_as = wil_mapped_as_page; 210562306a36Sopenharmony_ci wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, 210662306a36Sopenharmony_ci pa, len, ring_index); 210762306a36Sopenharmony_ci /* no need to check return code - 210862306a36Sopenharmony_ci * if it succeeded for 1-st descriptor, 210962306a36Sopenharmony_ci * it will succeed here too 211062306a36Sopenharmony_ci */ 211162306a36Sopenharmony_ci wil_tx_desc_offload_setup(d, skb); 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci /* for the last seg only */ 211462306a36Sopenharmony_ci d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); 211562306a36Sopenharmony_ci d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS); 211662306a36Sopenharmony_ci d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); 211762306a36Sopenharmony_ci *_d = *d; 211862306a36Sopenharmony_ci wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", ring_index, i); 211962306a36Sopenharmony_ci wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4, 212062306a36Sopenharmony_ci (const void *)d, sizeof(*d), false); 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci /* hold reference to skb 212362306a36Sopenharmony_ci * to prevent skb release before accounting 212462306a36Sopenharmony_ci * in case of immediate "tx done" 212562306a36Sopenharmony_ci */ 212662306a36Sopenharmony_ci ring->ctx[i].skb = skb_get(skb); 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci /* performance monitoring */ 212962306a36Sopenharmony_ci used = wil_ring_used_tx(ring); 213062306a36Sopenharmony_ci if (wil_val_in_range(wil->ring_idle_trsh, 213162306a36Sopenharmony_ci used, used + nr_frags + 1)) { 213262306a36Sopenharmony_ci txdata->idle += get_cycles() - txdata->last_idle; 213362306a36Sopenharmony_ci wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n", 213462306a36Sopenharmony_ci ring_index, used, used + nr_frags + 1); 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci /* Make sure to advance the head only after descriptor update is done. 213862306a36Sopenharmony_ci * This will prevent a race condition where the completion thread 213962306a36Sopenharmony_ci * will see the DU bit set from previous run and will handle the 214062306a36Sopenharmony_ci * skb before it was completed. 214162306a36Sopenharmony_ci */ 214262306a36Sopenharmony_ci wmb(); 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci /* advance swhead */ 214562306a36Sopenharmony_ci wil_ring_advance_head(ring, nr_frags + 1); 214662306a36Sopenharmony_ci wil_dbg_txrx(wil, "Tx[%2d] swhead %d -> %d\n", ring_index, swhead, 214762306a36Sopenharmony_ci ring->swhead); 214862306a36Sopenharmony_ci trace_wil6210_tx(ring_index, swhead, skb->len, nr_frags); 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci /* make sure all writes to descriptors (shared memory) are done before 215162306a36Sopenharmony_ci * committing them to HW 215262306a36Sopenharmony_ci */ 215362306a36Sopenharmony_ci wmb(); 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci if (wil->tx_latency) 215662306a36Sopenharmony_ci *(ktime_t *)&skb->cb = ktime_get(); 215762306a36Sopenharmony_ci else 215862306a36Sopenharmony_ci memset(skb->cb, 0, sizeof(ktime_t)); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci wil_w(wil, ring->hwtail, ring->swhead); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci return 0; 216362306a36Sopenharmony_ci dma_error: 216462306a36Sopenharmony_ci /* unmap what we have mapped */ 216562306a36Sopenharmony_ci nr_frags = f + 1; /* frags mapped + one for skb head */ 216662306a36Sopenharmony_ci for (f = 0; f < nr_frags; f++) { 216762306a36Sopenharmony_ci struct wil_ctx *ctx; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci i = (swhead + f) % ring->size; 217062306a36Sopenharmony_ci ctx = &ring->ctx[i]; 217162306a36Sopenharmony_ci _d = &ring->va[i].tx.legacy; 217262306a36Sopenharmony_ci *d = *_d; 217362306a36Sopenharmony_ci _d->dma.status = TX_DMA_STATUS_DU; 217462306a36Sopenharmony_ci wil->txrx_ops.tx_desc_unmap(dev, 217562306a36Sopenharmony_ci (union wil_tx_desc *)d, 217662306a36Sopenharmony_ci ctx); 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci return -EINVAL; 218262306a36Sopenharmony_ci} 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_cistatic int wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, 218562306a36Sopenharmony_ci struct wil_ring *ring, struct sk_buff *skb) 218662306a36Sopenharmony_ci{ 218762306a36Sopenharmony_ci int ring_index = ring - wil->ring_tx; 218862306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_index]; 218962306a36Sopenharmony_ci int rc; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci spin_lock(&txdata->lock); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci if (test_bit(wil_status_suspending, wil->status) || 219462306a36Sopenharmony_ci test_bit(wil_status_suspended, wil->status) || 219562306a36Sopenharmony_ci test_bit(wil_status_resuming, wil->status)) { 219662306a36Sopenharmony_ci wil_dbg_txrx(wil, 219762306a36Sopenharmony_ci "suspend/resume in progress. drop packet\n"); 219862306a36Sopenharmony_ci spin_unlock(&txdata->lock); 219962306a36Sopenharmony_ci return -EINVAL; 220062306a36Sopenharmony_ci } 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci rc = (skb_is_gso(skb) ? wil->txrx_ops.tx_ring_tso : __wil_tx_ring) 220362306a36Sopenharmony_ci (wil, vif, ring, skb); 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci spin_unlock(&txdata->lock); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci return rc; 220862306a36Sopenharmony_ci} 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci/* Check status of tx vrings and stop/wake net queues if needed 221162306a36Sopenharmony_ci * It will start/stop net queues of a specific VIF net_device. 221262306a36Sopenharmony_ci * 221362306a36Sopenharmony_ci * This function does one of two checks: 221462306a36Sopenharmony_ci * In case check_stop is true, will check if net queues need to be stopped. If 221562306a36Sopenharmony_ci * the conditions for stopping are met, netif_tx_stop_all_queues() is called. 221662306a36Sopenharmony_ci * In case check_stop is false, will check if net queues need to be waked. If 221762306a36Sopenharmony_ci * the conditions for waking are met, netif_tx_wake_all_queues() is called. 221862306a36Sopenharmony_ci * vring is the vring which is currently being modified by either adding 221962306a36Sopenharmony_ci * descriptors (tx) into it or removing descriptors (tx complete) from it. Can 222062306a36Sopenharmony_ci * be null when irrelevant (e.g. connect/disconnect events). 222162306a36Sopenharmony_ci * 222262306a36Sopenharmony_ci * The implementation is to stop net queues if modified vring has low 222362306a36Sopenharmony_ci * descriptor availability. Wake if all vrings are not in low descriptor 222462306a36Sopenharmony_ci * availability and modified vring has high descriptor availability. 222562306a36Sopenharmony_ci */ 222662306a36Sopenharmony_cistatic inline void __wil_update_net_queues(struct wil6210_priv *wil, 222762306a36Sopenharmony_ci struct wil6210_vif *vif, 222862306a36Sopenharmony_ci struct wil_ring *ring, 222962306a36Sopenharmony_ci bool check_stop) 223062306a36Sopenharmony_ci{ 223162306a36Sopenharmony_ci int i; 223262306a36Sopenharmony_ci int min_ring_id = wil_get_min_tx_ring_id(wil); 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci if (unlikely(!vif)) 223562306a36Sopenharmony_ci return; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci if (ring) 223862306a36Sopenharmony_ci wil_dbg_txrx(wil, "vring %d, mid %d, check_stop=%d, stopped=%d", 223962306a36Sopenharmony_ci (int)(ring - wil->ring_tx), vif->mid, check_stop, 224062306a36Sopenharmony_ci vif->net_queue_stopped); 224162306a36Sopenharmony_ci else 224262306a36Sopenharmony_ci wil_dbg_txrx(wil, "check_stop=%d, mid=%d, stopped=%d", 224362306a36Sopenharmony_ci check_stop, vif->mid, vif->net_queue_stopped); 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci if (ring && drop_if_ring_full) 224662306a36Sopenharmony_ci /* no need to stop/wake net queues */ 224762306a36Sopenharmony_ci return; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci if (check_stop == vif->net_queue_stopped) 225062306a36Sopenharmony_ci /* net queues already in desired state */ 225162306a36Sopenharmony_ci return; 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci if (check_stop) { 225462306a36Sopenharmony_ci if (!ring || unlikely(wil_ring_avail_low(ring))) { 225562306a36Sopenharmony_ci /* not enough room in the vring */ 225662306a36Sopenharmony_ci netif_tx_stop_all_queues(vif_to_ndev(vif)); 225762306a36Sopenharmony_ci vif->net_queue_stopped = true; 225862306a36Sopenharmony_ci wil_dbg_txrx(wil, "netif_tx_stop called\n"); 225962306a36Sopenharmony_ci } 226062306a36Sopenharmony_ci return; 226162306a36Sopenharmony_ci } 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci /* Do not wake the queues in suspend flow */ 226462306a36Sopenharmony_ci if (test_bit(wil_status_suspending, wil->status) || 226562306a36Sopenharmony_ci test_bit(wil_status_suspended, wil->status)) 226662306a36Sopenharmony_ci return; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci /* check wake */ 226962306a36Sopenharmony_ci for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { 227062306a36Sopenharmony_ci struct wil_ring *cur_ring = &wil->ring_tx[i]; 227162306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i]; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci if (txdata->mid != vif->mid || !cur_ring->va || 227462306a36Sopenharmony_ci !txdata->enabled || cur_ring == ring) 227562306a36Sopenharmony_ci continue; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci if (wil_ring_avail_low(cur_ring)) { 227862306a36Sopenharmony_ci wil_dbg_txrx(wil, "ring %d full, can't wake\n", 227962306a36Sopenharmony_ci (int)(cur_ring - wil->ring_tx)); 228062306a36Sopenharmony_ci return; 228162306a36Sopenharmony_ci } 228262306a36Sopenharmony_ci } 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci if (!ring || wil_ring_avail_high(ring)) { 228562306a36Sopenharmony_ci /* enough room in the ring */ 228662306a36Sopenharmony_ci wil_dbg_txrx(wil, "calling netif_tx_wake\n"); 228762306a36Sopenharmony_ci netif_tx_wake_all_queues(vif_to_ndev(vif)); 228862306a36Sopenharmony_ci vif->net_queue_stopped = false; 228962306a36Sopenharmony_ci } 229062306a36Sopenharmony_ci} 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_civoid wil_update_net_queues(struct wil6210_priv *wil, struct wil6210_vif *vif, 229362306a36Sopenharmony_ci struct wil_ring *ring, bool check_stop) 229462306a36Sopenharmony_ci{ 229562306a36Sopenharmony_ci spin_lock(&wil->net_queue_lock); 229662306a36Sopenharmony_ci __wil_update_net_queues(wil, vif, ring, check_stop); 229762306a36Sopenharmony_ci spin_unlock(&wil->net_queue_lock); 229862306a36Sopenharmony_ci} 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_civoid wil_update_net_queues_bh(struct wil6210_priv *wil, struct wil6210_vif *vif, 230162306a36Sopenharmony_ci struct wil_ring *ring, bool check_stop) 230262306a36Sopenharmony_ci{ 230362306a36Sopenharmony_ci spin_lock_bh(&wil->net_queue_lock); 230462306a36Sopenharmony_ci __wil_update_net_queues(wil, vif, ring, check_stop); 230562306a36Sopenharmony_ci spin_unlock_bh(&wil->net_queue_lock); 230662306a36Sopenharmony_ci} 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_cinetdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) 230962306a36Sopenharmony_ci{ 231062306a36Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(ndev); 231162306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 231262306a36Sopenharmony_ci const u8 *da = wil_skb_get_da(skb); 231362306a36Sopenharmony_ci bool bcast = is_multicast_ether_addr(da); 231462306a36Sopenharmony_ci struct wil_ring *ring; 231562306a36Sopenharmony_ci static bool pr_once_fw; 231662306a36Sopenharmony_ci int rc; 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci wil_dbg_txrx(wil, "start_xmit\n"); 231962306a36Sopenharmony_ci if (unlikely(!test_bit(wil_status_fwready, wil->status))) { 232062306a36Sopenharmony_ci if (!pr_once_fw) { 232162306a36Sopenharmony_ci wil_err(wil, "FW not ready\n"); 232262306a36Sopenharmony_ci pr_once_fw = true; 232362306a36Sopenharmony_ci } 232462306a36Sopenharmony_ci goto drop; 232562306a36Sopenharmony_ci } 232662306a36Sopenharmony_ci if (unlikely(!test_bit(wil_vif_fwconnected, vif->status))) { 232762306a36Sopenharmony_ci wil_dbg_ratelimited(wil, 232862306a36Sopenharmony_ci "VIF not connected, packet dropped\n"); 232962306a36Sopenharmony_ci goto drop; 233062306a36Sopenharmony_ci } 233162306a36Sopenharmony_ci if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_MONITOR)) { 233262306a36Sopenharmony_ci wil_err(wil, "Xmit in monitor mode not supported\n"); 233362306a36Sopenharmony_ci goto drop; 233462306a36Sopenharmony_ci } 233562306a36Sopenharmony_ci pr_once_fw = false; 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci /* find vring */ 233862306a36Sopenharmony_ci if (vif->wdev.iftype == NL80211_IFTYPE_STATION && !vif->pbss) { 233962306a36Sopenharmony_ci /* in STA mode (ESS), all to same VRING (to AP) */ 234062306a36Sopenharmony_ci ring = wil_find_tx_ring_sta(wil, vif, skb); 234162306a36Sopenharmony_ci } else if (bcast) { 234262306a36Sopenharmony_ci if (vif->pbss || wil_check_multicast_to_unicast(wil, skb)) 234362306a36Sopenharmony_ci /* in pbss, no bcast VRING - duplicate skb in 234462306a36Sopenharmony_ci * all stations VRINGs 234562306a36Sopenharmony_ci */ 234662306a36Sopenharmony_ci ring = wil_find_tx_bcast_2(wil, vif, skb); 234762306a36Sopenharmony_ci else if (vif->wdev.iftype == NL80211_IFTYPE_AP) 234862306a36Sopenharmony_ci /* AP has a dedicated bcast VRING */ 234962306a36Sopenharmony_ci ring = wil_find_tx_bcast_1(wil, vif, skb); 235062306a36Sopenharmony_ci else 235162306a36Sopenharmony_ci /* unexpected combination, fallback to duplicating 235262306a36Sopenharmony_ci * the skb in all stations VRINGs 235362306a36Sopenharmony_ci */ 235462306a36Sopenharmony_ci ring = wil_find_tx_bcast_2(wil, vif, skb); 235562306a36Sopenharmony_ci } else { 235662306a36Sopenharmony_ci /* unicast, find specific VRING by dest. address */ 235762306a36Sopenharmony_ci ring = wil_find_tx_ucast(wil, vif, skb); 235862306a36Sopenharmony_ci } 235962306a36Sopenharmony_ci if (unlikely(!ring)) { 236062306a36Sopenharmony_ci wil_dbg_txrx(wil, "No Tx RING found for %pM\n", da); 236162306a36Sopenharmony_ci goto drop; 236262306a36Sopenharmony_ci } 236362306a36Sopenharmony_ci /* set up vring entry */ 236462306a36Sopenharmony_ci rc = wil_tx_ring(wil, vif, ring, skb); 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci switch (rc) { 236762306a36Sopenharmony_ci case 0: 236862306a36Sopenharmony_ci /* shall we stop net queues? */ 236962306a36Sopenharmony_ci wil_update_net_queues_bh(wil, vif, ring, true); 237062306a36Sopenharmony_ci /* statistics will be updated on the tx_complete */ 237162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 237262306a36Sopenharmony_ci return NETDEV_TX_OK; 237362306a36Sopenharmony_ci case -ENOMEM: 237462306a36Sopenharmony_ci if (drop_if_ring_full) 237562306a36Sopenharmony_ci goto drop; 237662306a36Sopenharmony_ci return NETDEV_TX_BUSY; 237762306a36Sopenharmony_ci default: 237862306a36Sopenharmony_ci break; /* goto drop; */ 237962306a36Sopenharmony_ci } 238062306a36Sopenharmony_ci drop: 238162306a36Sopenharmony_ci ndev->stats.tx_dropped++; 238262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci return NET_XMIT_DROP; 238562306a36Sopenharmony_ci} 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_civoid wil_tx_latency_calc(struct wil6210_priv *wil, struct sk_buff *skb, 238862306a36Sopenharmony_ci struct wil_sta_info *sta) 238962306a36Sopenharmony_ci{ 239062306a36Sopenharmony_ci int skb_time_us; 239162306a36Sopenharmony_ci int bin; 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci if (!wil->tx_latency) 239462306a36Sopenharmony_ci return; 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci if (ktime_to_ms(*(ktime_t *)&skb->cb) == 0) 239762306a36Sopenharmony_ci return; 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci skb_time_us = ktime_us_delta(ktime_get(), *(ktime_t *)&skb->cb); 240062306a36Sopenharmony_ci bin = skb_time_us / wil->tx_latency_res; 240162306a36Sopenharmony_ci bin = min_t(int, bin, WIL_NUM_LATENCY_BINS - 1); 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci wil_dbg_txrx(wil, "skb time %dus => bin %d\n", skb_time_us, bin); 240462306a36Sopenharmony_ci sta->tx_latency_bins[bin]++; 240562306a36Sopenharmony_ci sta->stats.tx_latency_total_us += skb_time_us; 240662306a36Sopenharmony_ci if (skb_time_us < sta->stats.tx_latency_min_us) 240762306a36Sopenharmony_ci sta->stats.tx_latency_min_us = skb_time_us; 240862306a36Sopenharmony_ci if (skb_time_us > sta->stats.tx_latency_max_us) 240962306a36Sopenharmony_ci sta->stats.tx_latency_max_us = skb_time_us; 241062306a36Sopenharmony_ci} 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci/* Clean up transmitted skb's from the Tx VRING 241362306a36Sopenharmony_ci * 241462306a36Sopenharmony_ci * Return number of descriptors cleared 241562306a36Sopenharmony_ci * 241662306a36Sopenharmony_ci * Safe to call from IRQ 241762306a36Sopenharmony_ci */ 241862306a36Sopenharmony_ciint wil_tx_complete(struct wil6210_vif *vif, int ringid) 241962306a36Sopenharmony_ci{ 242062306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 242162306a36Sopenharmony_ci struct net_device *ndev = vif_to_ndev(vif); 242262306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 242362306a36Sopenharmony_ci struct wil_ring *vring = &wil->ring_tx[ringid]; 242462306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ringid]; 242562306a36Sopenharmony_ci int done = 0; 242662306a36Sopenharmony_ci int cid = wil->ring2cid_tid[ringid][0]; 242762306a36Sopenharmony_ci struct wil_net_stats *stats = NULL; 242862306a36Sopenharmony_ci volatile struct vring_tx_desc *_d; 242962306a36Sopenharmony_ci int used_before_complete; 243062306a36Sopenharmony_ci int used_new; 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci if (unlikely(!vring->va)) { 243362306a36Sopenharmony_ci wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); 243462306a36Sopenharmony_ci return 0; 243562306a36Sopenharmony_ci } 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci if (unlikely(!txdata->enabled)) { 243862306a36Sopenharmony_ci wil_info(wil, "Tx irq[%d]: vring disabled\n", ringid); 243962306a36Sopenharmony_ci return 0; 244062306a36Sopenharmony_ci } 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_ci wil_dbg_txrx(wil, "tx_complete: (%d)\n", ringid); 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci used_before_complete = wil_ring_used_tx(vring); 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci if (cid < wil->max_assoc_sta) 244762306a36Sopenharmony_ci stats = &wil->sta[cid].stats; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci while (!wil_ring_is_empty(vring)) { 245062306a36Sopenharmony_ci int new_swtail; 245162306a36Sopenharmony_ci struct wil_ctx *ctx = &vring->ctx[vring->swtail]; 245262306a36Sopenharmony_ci /* For the fragmented skb, HW will set DU bit only for the 245362306a36Sopenharmony_ci * last fragment. look for it. 245462306a36Sopenharmony_ci * In TSO the first DU will include hdr desc 245562306a36Sopenharmony_ci */ 245662306a36Sopenharmony_ci int lf = (vring->swtail + ctx->nr_frags) % vring->size; 245762306a36Sopenharmony_ci /* TODO: check we are not past head */ 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci _d = &vring->va[lf].tx.legacy; 246062306a36Sopenharmony_ci if (unlikely(!(_d->dma.status & TX_DMA_STATUS_DU))) 246162306a36Sopenharmony_ci break; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci new_swtail = (lf + 1) % vring->size; 246462306a36Sopenharmony_ci while (vring->swtail != new_swtail) { 246562306a36Sopenharmony_ci struct vring_tx_desc dd, *d = ⅆ 246662306a36Sopenharmony_ci u16 dmalen; 246762306a36Sopenharmony_ci struct sk_buff *skb; 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci ctx = &vring->ctx[vring->swtail]; 247062306a36Sopenharmony_ci skb = ctx->skb; 247162306a36Sopenharmony_ci _d = &vring->va[vring->swtail].tx.legacy; 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci *d = *_d; 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci dmalen = le16_to_cpu(d->dma.length); 247662306a36Sopenharmony_ci trace_wil6210_tx_done(ringid, vring->swtail, dmalen, 247762306a36Sopenharmony_ci d->dma.error); 247862306a36Sopenharmony_ci wil_dbg_txrx(wil, 247962306a36Sopenharmony_ci "TxC[%2d][%3d] : %d bytes, status 0x%02x err 0x%02x\n", 248062306a36Sopenharmony_ci ringid, vring->swtail, dmalen, 248162306a36Sopenharmony_ci d->dma.status, d->dma.error); 248262306a36Sopenharmony_ci wil_hex_dump_txrx("TxCD ", DUMP_PREFIX_NONE, 32, 4, 248362306a36Sopenharmony_ci (const void *)d, sizeof(*d), false); 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci wil->txrx_ops.tx_desc_unmap(dev, 248662306a36Sopenharmony_ci (union wil_tx_desc *)d, 248762306a36Sopenharmony_ci ctx); 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci if (skb) { 249062306a36Sopenharmony_ci if (likely(d->dma.error == 0)) { 249162306a36Sopenharmony_ci ndev->stats.tx_packets++; 249262306a36Sopenharmony_ci ndev->stats.tx_bytes += skb->len; 249362306a36Sopenharmony_ci if (stats) { 249462306a36Sopenharmony_ci stats->tx_packets++; 249562306a36Sopenharmony_ci stats->tx_bytes += skb->len; 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci wil_tx_latency_calc(wil, skb, 249862306a36Sopenharmony_ci &wil->sta[cid]); 249962306a36Sopenharmony_ci } 250062306a36Sopenharmony_ci } else { 250162306a36Sopenharmony_ci ndev->stats.tx_errors++; 250262306a36Sopenharmony_ci if (stats) 250362306a36Sopenharmony_ci stats->tx_errors++; 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci if (skb->protocol == cpu_to_be16(ETH_P_PAE)) 250762306a36Sopenharmony_ci wil_tx_complete_handle_eapol(vif, skb); 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci wil_consume_skb(skb, d->dma.error == 0); 251062306a36Sopenharmony_ci } 251162306a36Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 251262306a36Sopenharmony_ci /* Make sure the ctx is zeroed before updating the tail 251362306a36Sopenharmony_ci * to prevent a case where wil_tx_ring will see 251462306a36Sopenharmony_ci * this descriptor as used and handle it before ctx zero 251562306a36Sopenharmony_ci * is completed. 251662306a36Sopenharmony_ci */ 251762306a36Sopenharmony_ci wmb(); 251862306a36Sopenharmony_ci /* There is no need to touch HW descriptor: 251962306a36Sopenharmony_ci * - ststus bit TX_DMA_STATUS_DU is set by design, 252062306a36Sopenharmony_ci * so hardware will not try to process this desc., 252162306a36Sopenharmony_ci * - rest of descriptor will be initialized on Tx. 252262306a36Sopenharmony_ci */ 252362306a36Sopenharmony_ci vring->swtail = wil_ring_next_tail(vring); 252462306a36Sopenharmony_ci done++; 252562306a36Sopenharmony_ci } 252662306a36Sopenharmony_ci } 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci /* performance monitoring */ 252962306a36Sopenharmony_ci used_new = wil_ring_used_tx(vring); 253062306a36Sopenharmony_ci if (wil_val_in_range(wil->ring_idle_trsh, 253162306a36Sopenharmony_ci used_new, used_before_complete)) { 253262306a36Sopenharmony_ci wil_dbg_txrx(wil, "Ring[%2d] idle %d -> %d\n", 253362306a36Sopenharmony_ci ringid, used_before_complete, used_new); 253462306a36Sopenharmony_ci txdata->last_idle = get_cycles(); 253562306a36Sopenharmony_ci } 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci /* shall we wake net queues? */ 253862306a36Sopenharmony_ci if (done) 253962306a36Sopenharmony_ci wil_update_net_queues(wil, vif, vring, false); 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci return done; 254262306a36Sopenharmony_ci} 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_cistatic inline int wil_tx_init(struct wil6210_priv *wil) 254562306a36Sopenharmony_ci{ 254662306a36Sopenharmony_ci return 0; 254762306a36Sopenharmony_ci} 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_cistatic inline void wil_tx_fini(struct wil6210_priv *wil) {} 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_cistatic void wil_get_reorder_params(struct wil6210_priv *wil, 255262306a36Sopenharmony_ci struct sk_buff *skb, int *tid, int *cid, 255362306a36Sopenharmony_ci int *mid, u16 *seq, int *mcast, int *retry) 255462306a36Sopenharmony_ci{ 255562306a36Sopenharmony_ci struct vring_rx_desc *d = wil_skb_rxdesc(skb); 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci *tid = wil_rxdesc_tid(d); 255862306a36Sopenharmony_ci *cid = wil_skb_get_cid(skb); 255962306a36Sopenharmony_ci *mid = wil_rxdesc_mid(d); 256062306a36Sopenharmony_ci *seq = wil_rxdesc_seq(d); 256162306a36Sopenharmony_ci *mcast = wil_rxdesc_mcast(d); 256262306a36Sopenharmony_ci *retry = wil_rxdesc_retry(d); 256362306a36Sopenharmony_ci} 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_civoid wil_init_txrx_ops_legacy_dma(struct wil6210_priv *wil) 256662306a36Sopenharmony_ci{ 256762306a36Sopenharmony_ci wil->txrx_ops.configure_interrupt_moderation = 256862306a36Sopenharmony_ci wil_configure_interrupt_moderation; 256962306a36Sopenharmony_ci /* TX ops */ 257062306a36Sopenharmony_ci wil->txrx_ops.tx_desc_map = wil_tx_desc_map; 257162306a36Sopenharmony_ci wil->txrx_ops.tx_desc_unmap = wil_txdesc_unmap; 257262306a36Sopenharmony_ci wil->txrx_ops.tx_ring_tso = __wil_tx_vring_tso; 257362306a36Sopenharmony_ci wil->txrx_ops.ring_init_tx = wil_vring_init_tx; 257462306a36Sopenharmony_ci wil->txrx_ops.ring_fini_tx = wil_vring_free; 257562306a36Sopenharmony_ci wil->txrx_ops.ring_init_bcast = wil_vring_init_bcast; 257662306a36Sopenharmony_ci wil->txrx_ops.tx_init = wil_tx_init; 257762306a36Sopenharmony_ci wil->txrx_ops.tx_fini = wil_tx_fini; 257862306a36Sopenharmony_ci wil->txrx_ops.tx_ring_modify = wil_tx_vring_modify; 257962306a36Sopenharmony_ci /* RX ops */ 258062306a36Sopenharmony_ci wil->txrx_ops.rx_init = wil_rx_init; 258162306a36Sopenharmony_ci wil->txrx_ops.wmi_addba_rx_resp = wmi_addba_rx_resp; 258262306a36Sopenharmony_ci wil->txrx_ops.get_reorder_params = wil_get_reorder_params; 258362306a36Sopenharmony_ci wil->txrx_ops.get_netif_rx_params = 258462306a36Sopenharmony_ci wil_get_netif_rx_params; 258562306a36Sopenharmony_ci wil->txrx_ops.rx_crypto_check = wil_rx_crypto_check; 258662306a36Sopenharmony_ci wil->txrx_ops.rx_error_check = wil_rx_error_check; 258762306a36Sopenharmony_ci wil->txrx_ops.is_rx_idle = wil_is_rx_idle; 258862306a36Sopenharmony_ci wil->txrx_ops.rx_fini = wil_rx_fini; 258962306a36Sopenharmony_ci} 2590