/****************************************************************************** * * Copyright(c) 2015 - 2016 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * ******************************************************************************/ #define _RTL8822BE_XMIT_C_ #include /* PADAPTER, rtw_xmit.h and etc. */ #include /* HAL_DATA_TYPE */ #include "../halmac/halmac_api.h" #include "../rtl8822b.h" #include "rtl8822be.h" /* Debug Buffer Descriptor Ring */ /*#define BUF_DESC_DEBUG*/ #ifdef BUF_DESC_DEBUG #define buf_desc_debug(...) do {\ RTW_INFO("BUF_DESC:" __VA_ARGS__);\ } while (0) #else #define buf_desc_debug(...) do {} while (0) #endif static void rtl8822be_xmit_tasklet(void *priv) { _irqL irqL; _adapter *padapter = (_adapter *)priv; HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter); struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); /* try to deal with the pending packets */ rtl8822be_xmitframe_resume(padapter); } s32 rtl8822be_init_xmit_priv(_adapter *padapter) { s32 ret = _SUCCESS; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); _rtw_spinlock_init(&pdvobjpriv->irq_th_lock); #ifdef PLATFORM_LINUX tasklet_init(&pxmitpriv->xmit_tasklet, (void(*)(unsigned long))rtl8822be_xmit_tasklet, (unsigned long)padapter); #endif return ret; } void rtl8822be_free_xmit_priv(_adapter *padapter) { struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); _rtw_spinlock_free(&pdvobjpriv->irq_th_lock); } static s32 rtl8822be_enqueue_xmitbuf(struct rtw_tx_ring *ring, struct xmit_buf *pxmitbuf) { _irqL irqL; _queue *ppending_queue = &ring->queue; _func_enter_; if (pxmitbuf == NULL) return _FAIL; rtw_list_delete(&pxmitbuf->list); rtw_list_insert_tail(&(pxmitbuf->list), get_list_head(ppending_queue)); ring->qlen++; _func_exit_; return _SUCCESS; } struct xmit_buf *rtl8822be_dequeue_xmitbuf(struct rtw_tx_ring *ring) { _irqL irqL; _list *plist, *phead; struct xmit_buf *pxmitbuf = NULL; _queue *ppending_queue = &ring->queue; _func_enter_; if (_rtw_queue_empty(ppending_queue) == _TRUE) pxmitbuf = NULL; else { phead = get_list_head(ppending_queue); plist = get_next(phead); pxmitbuf = LIST_CONTAINOR(plist, struct xmit_buf, list); rtw_list_delete(&(pxmitbuf->list)); ring->qlen--; } _func_exit_; return pxmitbuf; } static u8 *get_txbd(_adapter *padapter, u8 q_idx) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct rtw_tx_ring *ring; u8 *ptxbd = NULL; int idx = 0; ring = &pxmitpriv->tx_ring[q_idx]; /* DO NOT use last entry. */ /* (len -1) to avoid wrap around overlap problem in cycler queue. */ if (ring->qlen == (ring->entries - 1)) { RTW_INFO("No more TX desc@%d, ring->idx = %d,idx = %d\n", q_idx, ring->idx, idx); return NULL; } if (q_idx == BCN_QUEUE_INX) idx = 0; else idx = (ring->idx + ring->qlen) % ring->entries; ptxbd = (u8 *)&ring->buf_desc[idx]; return ptxbd; } /* * Get txbd reg addr according to q_sel */ static u16 get_txbd_rw_reg(u16 q_idx) { u16 txbd_reg_addr = REG_BEQ_TXBD_IDX; switch (q_idx) { case BK_QUEUE_INX: txbd_reg_addr = REG_BKQ_TXBD_IDX; break; case BE_QUEUE_INX: txbd_reg_addr = REG_BEQ_TXBD_IDX; break; case VI_QUEUE_INX: txbd_reg_addr = REG_VIQ_TXBD_IDX; break; case VO_QUEUE_INX: txbd_reg_addr = REG_VOQ_TXBD_IDX; break; case BCN_QUEUE_INX: txbd_reg_addr = REG_BEQ_TXBD_IDX; /* need check */ break; case TXCMD_QUEUE_INX: txbd_reg_addr = REG_H2CQ_TXBD_IDX; break; case MGT_QUEUE_INX: txbd_reg_addr = REG_MGQ_TXBD_IDX; break; case HIGH_QUEUE_INX: txbd_reg_addr = REG_HI0Q_TXBD_IDX; /* need check */ break; default: break; } return txbd_reg_addr; } struct xmit_frame *__rtw_alloc_cmdxmitframe_8822be(struct xmit_priv *pxmitpriv, enum cmdbuf_type buf_type) { _adapter *padapter; u16 queue_idx = BCN_QUEUE_INX; u8 *ptxdesc = NULL; padapter = GET_PRIMARY_ADAPTER(pxmitpriv->adapter); ptxdesc = get_txbd(padapter, BCN_QUEUE_INX); /* set OWN bit in Beacon tx descriptor */ if (ptxdesc != NULL) SET_TX_BD_OWN(ptxdesc, 0); else return NULL; return __rtw_alloc_cmdxmitframe(pxmitpriv, CMDBUF_BEACON); } /* * Update Read/Write pointer * Read pointer is h/w descriptor index * Write pointer is host desciptor index: * For tx side, if own bit is set in packet index n, * host pointer (write pointer) point to index n + 1.) */ void fill_txbd_own(_adapter *padapter, u8 *txbd, u16 queue_idx, struct rtw_tx_ring *ptxring) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct rtw_tx_ring *ring; u16 host_wp = 0; if (queue_idx == BCN_QUEUE_INX) { SET_TX_BD_OWN(txbd, 1); /* kick start */ rtw_write8(padapter, REG_RX_RXBD_NUM + 1, rtw_read8(padapter, REG_RX_RXBD_NUM + 1) | BIT(4)); return; } /* * update h/w index * for tx side, if own bit is set in packet index n, * host pointer (write pointer) point to index n + 1. */ /* for current tx packet, enqueue has been ring->qlen++ before. * so, host_wp = ring->idx + ring->qlen. */ host_wp = (ptxring->idx + ptxring->qlen) % ptxring->entries; rtw_write16(padapter, get_txbd_rw_reg(queue_idx), host_wp); } static u16 ffaddr2dma(u32 addr) { u16 dma_ctrl; switch (addr) { case VO_QUEUE_INX: dma_ctrl = BIT3; break; case VI_QUEUE_INX: dma_ctrl = BIT2; break; case BE_QUEUE_INX: dma_ctrl = BIT1; break; case BK_QUEUE_INX: dma_ctrl = BIT0; break; case BCN_QUEUE_INX: dma_ctrl = BIT4; break; case MGT_QUEUE_INX: dma_ctrl = BIT6; break; case HIGH_QUEUE_INX: dma_ctrl = BIT7; break; default: dma_ctrl = 0; break; } return dma_ctrl; } /* * Fill tx buffer desciptor. Map each buffer address in tx buffer descriptor * segment. Designed for tx buffer descriptor architecture * Input *pmem: pointer to the Tx Buffer Descriptor */ static void rtl8822be_update_txbd(struct xmit_frame *pxmitframe, u8 *txbd, s32 sz) { _adapter *padapter = pxmitframe->padapter; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); dma_addr_t mapping; u32 i = 0; u16 seg_num = ((TX_BUFFER_SEG_NUM == 0) ? 2 : ((TX_BUFFER_SEG_NUM == 1) ? 4 : 8)); u16 tx_page_size_reg = 1; u16 page_size_length = 0; /* map TX DESC buf_addr (including TX DESC + tx data) */ mapping = pci_map_single(pdvobjpriv->ppcidev, pxmitframe->buf_addr , sz + TX_WIFI_INFO_SIZE, PCI_DMA_TODEVICE); /* Calculate page size. * Total buffer length including TX_WIFI_INFO and PacketLen */ if (tx_page_size_reg > 0) { page_size_length = (sz + TX_WIFI_INFO_SIZE) / (tx_page_size_reg * 128); if (((sz + TX_WIFI_INFO_SIZE) % (tx_page_size_reg * 128)) > 0) page_size_length++; } /* * Reset all tx buffer desciprtor content * -- Reset first element */ SET_TX_BD_TX_BUFF_SIZE0(txbd, 0); SET_TX_BD_PSB(txbd, 0); SET_TX_BD_OWN(txbd, 0); /* -- Reset second and other element */ for (i = 1 ; i < seg_num ; i++) { SET_TXBUFFER_DESC_LEN_WITH_OFFSET(txbd, i, 0); SET_TXBUFFER_DESC_AMSDU_WITH_OFFSET(txbd, i, 0); SET_TXBUFFER_DESC_ADD_LOW_WITH_OFFSET(txbd, i, 0); } /* * Fill buffer length of the first buffer, * For 8822be, it is required that TX_WIFI_INFO is put in first segment, * and the size of the first segment cannot be larger than * TX_WIFI_INFO_SIZE. */ SET_TX_BD_TX_BUFF_SIZE0(txbd, TX_WIFI_INFO_SIZE); SET_TX_BD_PSB(txbd, page_size_length); /* starting addr of TXDESC */ SET_TX_BD_PHYSICAL_ADDR0_LOW(txbd, mapping); /* * It is assumed that in linux implementation, packet is coalesced * in only one buffer. Extension mode is not supported here */ SET_TXBUFFER_DESC_LEN_WITH_OFFSET(txbd, 1, sz); /* don't using extendsion mode. */ SET_TXBUFFER_DESC_AMSDU_WITH_OFFSET(txbd, 1, 0); SET_TXBUFFER_DESC_ADD_LOW_WITH_OFFSET(txbd, 1, mapping + TX_WIFI_INFO_SIZE); /* pkt */ /*buf_desc_debug("TX:%s, txbd = 0x%p\n", __FUNCTION__, txbd);*/ buf_desc_debug("%s, txbd = 0x%08x\n", __func__, txbd); buf_desc_debug("TXBD:, 00h(0x%08x)\n", *((u32 *)(txbd))); buf_desc_debug("TXBD:, 04h(0x%08x)\n", *((u32 *)(txbd + 4))); buf_desc_debug("TXBD:, 08h(0x%08x)\n", *((u32 *)(txbd + 8))); buf_desc_debug("TXBD:, 12h(0x%08x)\n", *((u32 *)(txbd + 12))); } static s32 update_txdesc(struct xmit_frame *pxmitframe, s32 sz) { uint qsel; u8 data_rate, pwr_status; _adapter *padapter = pxmitframe->padapter; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); struct pkt_attrib *pattrib = &pxmitframe->attrib; HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter); struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); u8 *ptxdesc; sint bmcst = IS_MCAST(pattrib->ra); u16 SWDefineContent = 0x0; u8 DriverFixedRate = 0x0; ptxdesc = pxmitframe->buf_addr; _rtw_memset(ptxdesc, 0, TXDESC_SIZE); /* offset 0 */ /*SET_TX_DESC_FIRST_SEG_8812(ptxdesc, 1);*/ SET_TX_DESC_LS_8822B(ptxdesc, 1); /*SET_TX_DESC_OWN_8812(ptxdesc, 1);*/ SET_TX_DESC_TXPKTSIZE_8822B(ptxdesc, sz); SET_TX_DESC_OFFSET_8822B(ptxdesc, TXDESC_SIZE); #ifdef CONFIG_TX_EARLY_MODE SET_TX_DESC_PKT_OFFSET_8812(ptxdesc, 1); SET_TX_DESC_OFFSET_8822B(ptxdesc, TXDESC_SIZE + EARLY_MODE_INFO_SIZE); #endif if (bmcst) SET_TX_DESC_BMC_8822B(ptxdesc, 1); SET_TX_DESC_MACID_8822B(ptxdesc, pattrib->mac_id); SET_TX_DESC_RATE_ID_8822B(ptxdesc, pattrib->raid); SET_TX_DESC_QSEL_8822B(ptxdesc, pattrib->qsel); if (!pattrib->qos_en) { /* Hw set sequence number */ SET_TX_DESC_EN_HWSEQ_8822B(ptxdesc, 1); SET_TX_DESC_EN_HWEXSEQ_8822B(ptxdesc, 0); SET_TX_DESC_DISQSELSEQ_8822B(ptxdesc, 1); SET_TX_DESC_HW_SSN_SEL_8822B(ptxdesc, 0); } else SET_TX_DESC_SW_SEQ_8822B(ptxdesc, pattrib->seqnum); if ((pxmitframe->frame_tag & 0x0f) == DATA_FRAMETAG) { rtl8822b_fill_txdesc_sectype(pattrib, ptxdesc); rtl8822b_fill_txdesc_vcs(padapter, pattrib, ptxdesc); #ifdef CONFIG_CONCURRENT_MODE if (bmcst) rtl8822b_fill_txdesc_force_bmc_camid(pattrib, ptxdesc); #endif if ((pattrib->ether_type != 0x888e) && (pattrib->ether_type != 0x0806) && (pattrib->ether_type != 0x88b4) && (pattrib->dhcp_pkt != 1) #ifdef CONFIG_AUTO_AP_MODE && (pattrib->pctrl != _TRUE) #endif ) { /* Non EAP & ARP & DHCP type data packet */ if (pattrib->ampdu_en == _TRUE) { /* 8822b does NOT support AGG broadcast pkt */ if (!bmcst) SET_TX_DESC_AGG_EN_8822B(ptxdesc, 1); SET_TX_DESC_MAX_AGG_NUM_8822B(ptxdesc, 0x1f); /* Set A-MPDU aggregation. */ SET_TX_DESC_AMPDU_DENSITY_8822B(ptxdesc, pattrib->ampdu_spacing); } else SET_TX_DESC_BK_8822B(ptxdesc, 1); rtl8822b_fill_txdesc_phy(padapter, pattrib, ptxdesc); /* DATA Rate FB LMT */ /* compatibility for MCC consideration, use pmlmeext->cur_channel */ if (pmlmeext->cur_channel > 14) /* for 5G. OFMD 6M */ SET_TX_DESC_DATA_RTY_LOWEST_RATE_8822B( ptxdesc, 4); else /* for 2.4G. CCK 1M */ SET_TX_DESC_DATA_RTY_LOWEST_RATE_8822B( ptxdesc, 0); if (pHalData->fw_ractrl == _FALSE) { SET_TX_DESC_USE_RATE_8822B(ptxdesc, 1); DriverFixedRate = 0x01; if (pHalData->INIDATA_RATE[pattrib->mac_id] & BIT(7)) SET_TX_DESC_DATA_SHORT_8822B( ptxdesc, 1); SET_TX_DESC_DATARATE_8822B(ptxdesc, pHalData->INIDATA_RATE[pattrib->mac_id] & 0x7F); } if (padapter->fix_rate != 0xFF) { /* modify data rate by iwpriv */ SET_TX_DESC_USE_RATE_8822B(ptxdesc, 1); DriverFixedRate = 0x01; if (padapter->fix_rate & BIT(7)) SET_TX_DESC_DATA_SHORT_8822B( ptxdesc, 1); SET_TX_DESC_DATARATE_8822B(ptxdesc, (padapter->fix_rate & 0x7F)); if (!padapter->data_fb) SET_TX_DESC_DISDATAFB_8822B(ptxdesc, 1); } if (pattrib->ldpc) SET_TX_DESC_DATA_LDPC_8822B(ptxdesc, 1); if (pattrib->stbc) SET_TX_DESC_DATA_STBC_8822B(ptxdesc, 1); } else { /* * EAP data packet and ARP packet and DHCP. * Use the 1M data rate to send the EAP/ARP packet. * This will maybe make the handshake smooth. */ SET_TX_DESC_USE_RATE_8822B(ptxdesc, 1); DriverFixedRate = 0x01; SET_TX_DESC_BK_8822B(ptxdesc, 1); /* HW will ignore this setting if the transmission rate * is legacy OFDM. */ if (pmlmeinfo->preamble_mode == PREAMBLE_SHORT) SET_TX_DESC_DATA_SHORT_8822B(ptxdesc, 1); SET_TX_DESC_DATARATE_8822B(ptxdesc, MRateToHwRate(pmlmeext->tx_rate)); } #ifdef CONFIG_TDLS #ifdef CONFIG_XMIT_ACK /* CCX-TXRPT ack for xmit mgmt frames. */ if (pxmitframe->ack_report) { SET_TX_DESC_SPE_RPT_8812(ptxdesc, 1); #ifdef DBG_CCX RTW_INFO("%s set tx report\n", __func__); #endif } #endif /* CONFIG_XMIT_ACK */ #endif } else if ((pxmitframe->frame_tag & 0x0f) == MGNT_FRAMETAG) { SET_TX_DESC_USE_RATE_8822B(ptxdesc, 1); DriverFixedRate = 0x01; #ifdef CONFIG_INTEL_PROXIM if ((padapter->proximity.proxim_on == _TRUE) && (pattrib->intel_proxim == _TRUE)) { RTW_INFO("\n %s pattrib->rate=%d\n", __func__, pattrib->rate); SET_TX_DESC_DATARATE_8822B(ptxdesc, pattrib->rate); } else #endif SET_TX_DESC_DATARATE_8822B(ptxdesc, MRateToHwRate(pattrib->rate)); #if 1 /* 8822bu add */ /* VHT NDPA or HT NDPA Packet for Beamformer. */ if ((pattrib->subtype == WIFI_NDPA) || ((pattrib->subtype == WIFI_ACTION_NOACK) && (pattrib->order == 1))) { SET_TX_DESC_NAVUSEHDR_8822B(ptxdesc, 1); SET_TX_DESC_DATA_BW_8822B(ptxdesc, rtl8822b_bw_mapping(padapter, pattrib)); SET_TX_DESC_RTS_SC_8822B(ptxdesc, rtl8822b_sc_mapping(padapter, pattrib)); SET_TX_DESC_RTY_LMT_EN_8822B(ptxdesc, 1); SET_TX_DESC_RTS_DATA_RTY_LMT_8822B(ptxdesc, 5); SET_TX_DESC_DISRTSFB_8822B(ptxdesc, 1); SET_TX_DESC_NDPA_8822B(ptxdesc, 1); } else { SET_TX_DESC_RTY_LMT_EN_8822B(ptxdesc, 1); if (pattrib->retry_ctrl == _TRUE) SET_TX_DESC_RTS_DATA_RTY_LMT_8822B(ptxdesc, 6); else SET_TX_DESC_RTS_DATA_RTY_LMT_8822B(ptxdesc, 12); } #endif #ifdef CONFIG_XMIT_ACK /* CCX-TXRPT ack for xmit mgmt frames. */ if (pxmitframe->ack_report) { SET_TX_DESC_SPE_RPT_8822B(ptxdesc, 1); #ifdef DBG_CCX RTW_INFO("%s set tx report\n", __func__); #endif } #endif /* CONFIG_XMIT_ACK */ } else if ((pxmitframe->frame_tag & 0x0f) == TXAGG_FRAMETAG) RTW_INFO("pxmitframe->frame_tag == TXAGG_FRAMETAG\n"); #ifdef CONFIG_MP_INCLUDED else if (((pxmitframe->frame_tag & 0x0f) == MP_FRAMETAG) && (padapter->registrypriv.mp_mode == 1)) fill_txdesc_for_mp(padapter, ptxdesc); #endif else { RTW_INFO("pxmitframe->frame_tag = %d\n", pxmitframe->frame_tag); SET_TX_DESC_USE_RATE_8822B(ptxdesc, 1); DriverFixedRate = 0x01; SET_TX_DESC_DATARATE_8822B(ptxdesc, MRateToHwRate(pmlmeext->tx_rate)); } #ifdef CONFIG_ANTENNA_DIVERSITY ODM_SetTxAntByTxInfo(&pHalData->odmpriv, ptxdesc, pxmitframe->attrib.mac_id); #endif #ifdef CONFIG_BEAMFORMING SET_TX_DESC_G_ID_8822B(ptxdesc, pattrib->txbf_g_id); SET_TX_DESC_P_AID_8822B(ptxdesc, pattrib->txbf_p_aid); #endif /*SET_TX_DESC_TX_BUFFER_SIZE_8812(ptxdesc, sz);*/ if (DriverFixedRate) SWDefineContent |= 0x01; SET_TX_DESC_SW_DEFINE_8822B(ptxdesc, SWDefineContent); rtl8822b_cal_txdesc_chksum(padapter, ptxdesc); rtl8822b_dbg_dump_tx_desc(padapter, pxmitframe->frame_tag, ptxdesc); return 0; } s32 rtl8822be_dump_xframe(_adapter *padapter, struct xmit_frame *pxmitframe) { s32 ret = _SUCCESS; s32 inner_ret = _SUCCESS; _irqL irqL; int t, sz, w_sz, pull = 0; u32 ff_hwaddr; struct xmit_buf *pxmitbuf = pxmitframe->pxmitbuf; struct pkt_attrib *pattrib = &pxmitframe->attrib; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); struct security_priv *psecuritypriv = &padapter->securitypriv; u8 *txbd; struct rtw_tx_ring *ptx_ring; DBG_COUNTER(padapter->tx_logs.intf_tx_dump_xframe); RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtl8822be_dump_xframe()\n")); if ((pxmitframe->frame_tag == DATA_FRAMETAG) && (pxmitframe->attrib.ether_type != 0x0806) && (pxmitframe->attrib.ether_type != 0x888e) && (pxmitframe->attrib.dhcp_pkt != 1)) rtw_issue_addbareq_cmd(padapter, pxmitframe); for (t = 0; t < pattrib->nr_frags; t++) { if (inner_ret != _SUCCESS && ret == _SUCCESS) ret = _FAIL; if (t != (pattrib->nr_frags - 1)) { RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("pattrib->nr_frags=%d\n", pattrib->nr_frags)); sz = pxmitpriv->frag_len - 4; if (!psecuritypriv->sw_encrypt) sz -= pattrib->icv_len; } else { /* no frag */ sz = pattrib->last_txcmdsz; } ff_hwaddr = rtw_get_ff_hwaddr(pxmitframe); _enter_critical(&pdvobjpriv->irq_th_lock, &irqL); txbd = get_txbd(GET_PRIMARY_ADAPTER(padapter), ff_hwaddr); ptx_ring = &(GET_PRIMARY_ADAPTER(padapter)->xmitpriv.tx_ring[ff_hwaddr]); #ifndef CONFIG_BCN_ICF if (BCN_QUEUE_INX == ff_hwaddr) padapter->xmitpriv.beaconDMAing = _TRUE; #endif if (txbd == NULL) { _exit_critical(&pdvobjpriv->irq_th_lock, &irqL); rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_TX_DESC_NA); rtw_free_xmitbuf(pxmitpriv, pxmitbuf); RTW_INFO("##### Tx desc unavailable !#####\n"); DBG_COUNTER(padapter->tx_logs.intf_tx_dump_xframe_err_txdesc); break; } if (pattrib->qsel != HALMAC_TXDESC_QSEL_H2C_CMD) update_txdesc(pxmitframe, sz); /* rtl8822be_update_txbd() must be called after update_txdesc(). * It rely on rtl8822be_update_txbd() to map it into non cache * memory */ rtl8822be_update_txbd(pxmitframe, txbd, sz); if (pxmitbuf->buf_tag != XMITBUF_CMD) rtl8822be_enqueue_xmitbuf(ptx_ring, pxmitbuf); pxmitbuf->len = sz + TX_WIFI_INFO_SIZE; w_sz = sz; /* Please comment here */ wmb(); fill_txbd_own(padapter, txbd, ff_hwaddr, ptx_ring); _exit_critical(&pdvobjpriv->irq_th_lock, &irqL); inner_ret = rtw_write_port(padapter, ff_hwaddr, w_sz, (unsigned char *)pxmitbuf); if (inner_ret != _SUCCESS) DBG_COUNTER(padapter->tx_logs.intf_tx_dump_xframe_err_port); rtw_count_tx_stats(padapter, pxmitframe, sz); RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_write_port, w_sz=%d\n", w_sz)); } rtw_free_xmitframe(pxmitpriv, pxmitframe); if (ret != _SUCCESS) rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_UNKNOWN); return ret; } /* * Packet should not be dequeued if there is no available descriptor * return: _SUCCESS if there is available descriptor */ static u8 check_tx_desc_resource(_adapter *padapter, int prio) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct rtw_tx_ring *ring; ring = &pxmitpriv->tx_ring[prio]; /* * for now we reserve two free descriptor as a safety boundary * between the tail and the head */ if ((ring->entries - ring->qlen) >= 2) return _TRUE; else return _FALSE; } static u8 check_nic_enough_desc_all(_adapter *padapter) { u8 status = (check_tx_desc_resource(padapter, VI_QUEUE_INX) && check_tx_desc_resource(padapter, VO_QUEUE_INX) && check_tx_desc_resource(padapter, BE_QUEUE_INX) && check_tx_desc_resource(padapter, BK_QUEUE_INX) && check_tx_desc_resource(padapter, MGT_QUEUE_INX) && check_tx_desc_resource(padapter, TXCMD_QUEUE_INX) && check_tx_desc_resource(padapter, HIGH_QUEUE_INX)); return status; } #ifdef TX_AMSDU static s32 xmitframe_amsdu_direct(_adapter *padapter, struct xmit_frame *pxmitframe) { struct xmit_buf *pxmitbuf = pxmitframe->pxmitbuf; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; s32 res = _SUCCESS; res = rtw_xmitframe_coalesce_amsdu(padapter, pxmitframe, NULL); if(res == _FAIL) goto free_frame; DBG_COUNTER(padapter->tx_logs.intf_tx_dequeue); res = rtl8822be_dump_xframe(padapter, pxmitframe); if (res == _FAIL) { DBG_COUNTER(padapter->tx_logs.intf_tx_dequeue_err_coalesce); goto free_frame; } return res; free_frame: rtw_free_xmitbuf(pxmitpriv, pxmitbuf); rtw_free_xmitframe(pxmitpriv, pxmitframe); return res; } #endif static s32 xmitframe_direct(_adapter *padapter, struct xmit_frame *pxmitframe) { s32 res = _SUCCESS; DBG_COUNTER(padapter->tx_logs.intf_tx_direct); res = rtw_xmitframe_coalesce(padapter, pxmitframe->pkt, pxmitframe); if (res == _SUCCESS) rtl8822be_dump_xframe(padapter, pxmitframe); else DBG_COUNTER(padapter->tx_logs.intf_tx_direct_err_coalesce); return res; } void rtl8822be_xmitframe_resume(_adapter *padapter) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct xmit_frame *pxmitframe = NULL; struct xmit_buf *pxmitbuf = NULL; int res = _SUCCESS, xcnt = 0; #ifdef TX_AMSDU struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); int tx_amsdu = padapter->tx_amsdu; int tx_amsdu_rate = padapter->tx_amsdu_rate; int current_tx_rate = pdvobjpriv->traffic_stat.cur_tx_tp; struct pkt_attrib *pattrib = NULL; struct xmit_frame *pxmitframe_next = NULL; struct xmit_buf *pxmitbuf_next = NULL; struct pkt_attrib *pattrib_next = NULL; int num_frame = 0; u8 amsdu_timeout = 0; #endif RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("%s()\n", __func__)); while (1) { if (RTW_CANNOT_RUN(padapter)) { RTW_INFO("%s => bDriverStopped or bSurpriseRemoved\n", __func__); break; } if (!check_nic_enough_desc_all(padapter)) break; pxmitbuf = rtw_alloc_xmitbuf(pxmitpriv); if (!pxmitbuf) break; #ifdef TX_AMSDU if(tx_amsdu == 0) goto dump_pkt; if(!check_fwstate(pmlmepriv, WIFI_STATION_STATE)) goto dump_pkt; pxmitframe = rtw_get_xframe(pxmitpriv, &num_frame); if(num_frame == 0 || pxmitframe == NULL || !check_amsdu(pxmitframe)) goto dump_pkt; if(tx_amsdu == 1) { pxmitframe = rtw_dequeue_xframe(pxmitpriv, pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); if (pxmitframe) { pxmitframe->pxmitbuf = pxmitbuf; pxmitframe->buf_addr = pxmitbuf->pbuf; pxmitbuf->priv_data = pxmitframe; xmitframe_amsdu_direct(padapter, pxmitframe); pxmitpriv->amsdu_debug_coalesce_one++; continue; } else { rtw_free_xmitbuf(pxmitpriv, pxmitbuf); break; } } else if(tx_amsdu == 2 && ((tx_amsdu_rate == 0) || (current_tx_rate > tx_amsdu_rate))) { if(num_frame == 1) { pattrib = &pxmitframe->attrib; amsdu_timeout = rtw_amsdu_get_timer_status(padapter, pattrib->priority); if(amsdu_timeout == RTW_AMSDU_TIMER_UNSET) { rtw_free_xmitbuf(pxmitpriv, pxmitbuf); rtw_amsdu_set_timer_status(padapter, pattrib->priority, RTW_AMSDU_TIMER_SETTING); rtw_amsdu_set_timer(padapter, pattrib->priority); pxmitpriv->amsdu_debug_set_timer++; break; } else if(amsdu_timeout == RTW_AMSDU_TIMER_SETTING) { rtw_free_xmitbuf(pxmitpriv, pxmitbuf); break; } else if(amsdu_timeout == RTW_AMSDU_TIMER_TIMEOUT) { rtw_amsdu_set_timer_status(padapter, pattrib->priority, RTW_AMSDU_TIMER_UNSET); pxmitpriv->amsdu_debug_timeout++; pxmitframe = rtw_dequeue_xframe(pxmitpriv, pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); if (pxmitframe) { pxmitframe->pxmitbuf = pxmitbuf; pxmitframe->buf_addr = pxmitbuf->pbuf; pxmitbuf->priv_data = pxmitframe; xmitframe_amsdu_direct(padapter, pxmitframe); break; } else { rtw_free_xmitbuf(pxmitpriv, pxmitbuf); break; } } } else/* num_frame > 1*/ { pxmitframe = rtw_dequeue_xframe(pxmitpriv, pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); if(!pxmitframe) { rtw_free_xmitbuf(pxmitpriv, pxmitbuf); break; } pxmitframe->pxmitbuf = pxmitbuf; pxmitframe->buf_addr = pxmitbuf->pbuf; pxmitbuf->priv_data = pxmitframe; pxmitframe_next = rtw_get_xframe(pxmitpriv, &num_frame); if(num_frame == 0) { xmitframe_amsdu_direct(padapter, pxmitframe); pxmitpriv->amsdu_debug_coalesce_one++; break; } if(!check_amsdu(pxmitframe_next)) { xmitframe_amsdu_direct(padapter, pxmitframe); pxmitpriv->amsdu_debug_coalesce_one++; continue; } else { pxmitbuf_next = rtw_alloc_xmitbuf(pxmitpriv); if (!pxmitbuf_next) { xmitframe_amsdu_direct(padapter, pxmitframe); pxmitpriv->amsdu_debug_coalesce_one++; continue; } pxmitframe_next = rtw_dequeue_xframe(pxmitpriv, pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); if(!pxmitframe_next) { rtw_free_xmitbuf(pxmitpriv, pxmitbuf_next); xmitframe_amsdu_direct(padapter, pxmitframe); pxmitpriv->amsdu_debug_coalesce_one++; continue; } pxmitframe_next->pxmitbuf = pxmitbuf_next; pxmitframe_next->buf_addr = pxmitbuf_next->pbuf; pxmitbuf_next->priv_data = pxmitframe_next; rtw_xmitframe_coalesce_amsdu(padapter, pxmitframe_next , pxmitframe); rtw_free_xmitframe(pxmitpriv, pxmitframe); rtw_free_xmitbuf(pxmitpriv, pxmitbuf); rtl8822be_dump_xframe(padapter, pxmitframe_next); pxmitpriv->amsdu_debug_coalesce_two++; continue; } } } dump_pkt: #endif pxmitframe = rtw_dequeue_xframe(pxmitpriv, pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); if (pxmitframe) { pxmitframe->pxmitbuf = pxmitbuf; pxmitframe->buf_addr = pxmitbuf->pbuf; pxmitbuf->priv_data = pxmitframe; if ((pxmitframe->frame_tag & 0x0f) == DATA_FRAMETAG) { if (pxmitframe->attrib.priority <= 15) { /* TID0~15 */ res = rtw_xmitframe_coalesce(padapter, pxmitframe->pkt, pxmitframe); } /* always return ndis_packet after * rtw_xmitframe_coalesce */ rtw_os_xmit_complete(padapter, pxmitframe); } DBG_COUNTER(padapter->tx_logs.intf_tx_dequeue); RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("%s(): rtl8822be_dump_xframe\n", __func__)); if (res == _SUCCESS) rtl8822be_dump_xframe(padapter, pxmitframe); else { DBG_COUNTER(padapter->tx_logs.intf_tx_dequeue_err_coalesce); rtw_free_xmitbuf(pxmitpriv, pxmitbuf); rtw_free_xmitframe(pxmitpriv, pxmitframe); } xcnt++; } else { rtw_free_xmitbuf(pxmitpriv, pxmitbuf); break; } } } static u8 check_nic_enough_desc(_adapter *padapter, struct pkt_attrib *pattrib) { u32 prio; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct rtw_tx_ring *ring; switch (pattrib->qsel) { case 0: case 3: prio = BE_QUEUE_INX; break; case 1: case 2: prio = BK_QUEUE_INX; break; case 4: case 5: prio = VI_QUEUE_INX; break; case 6: case 7: prio = VO_QUEUE_INX; break; default: prio = BE_QUEUE_INX; break; } ring = &pxmitpriv->tx_ring[prio]; /* * for now we reserve two free descriptor as a safety boundary * between the tail and the head */ if ((ring->entries - ring->qlen) >= 2) return _TRUE; else return _FALSE; } /* * Return * _TRUE dump packet directly * _FALSE enqueue packet */ static s32 pre_xmitframe(_adapter *padapter, struct xmit_frame *pxmitframe) { _irqL irqL; s32 res; struct xmit_buf *pxmitbuf = NULL; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct pkt_attrib *pattrib = &pxmitframe->attrib; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; #ifdef TX_AMSDU struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); int tx_amsdu = padapter->tx_amsdu; int tx_amsdu_rate = padapter->tx_amsdu_rate; int current_tx_rate = pdvobjpriv->traffic_stat.cur_tx_tp; u8 amsdu_timeout = 0; #endif _enter_critical_bh(&pxmitpriv->lock, &irqL); if ((rtw_txframes_sta_ac_pending(padapter, pattrib) > 0) || (check_nic_enough_desc(padapter, pattrib) == _FALSE)) { DBG_COUNTER(padapter->tx_logs.intf_tx_pending_ac); goto enqueue; } if (rtw_xmit_ac_blocked(padapter) == _TRUE) { DBG_COUNTER(padapter->tx_logs.intf_tx_pending_fw_under_survey); goto enqueue; } if (padapter->dvobj->iface_state.lg_sta_num) { DBG_COUNTER(padapter->tx_logs.intf_tx_pending_fw_under_linking); goto enqueue; } #ifdef TX_AMSDU if(check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { if(tx_amsdu == 1) goto enqueue; else if(tx_amsdu == 2 && (tx_amsdu_rate == 0 || current_tx_rate > tx_amsdu_rate)) goto enqueue; } #endif pxmitbuf = rtw_alloc_xmitbuf(pxmitpriv); if (pxmitbuf == NULL) { DBG_COUNTER(padapter->tx_logs.intf_tx_pending_xmitbuf); goto enqueue; } _exit_critical_bh(&pxmitpriv->lock, &irqL); pxmitframe->pxmitbuf = pxmitbuf; pxmitframe->buf_addr = pxmitbuf->pbuf; pxmitbuf->priv_data = pxmitframe; if (xmitframe_direct(padapter, pxmitframe) != _SUCCESS) { rtw_free_xmitbuf(pxmitpriv, pxmitbuf); rtw_free_xmitframe(pxmitpriv, pxmitframe); } return _TRUE; enqueue: res = rtw_xmitframe_enqueue(padapter, pxmitframe); #ifdef TX_AMSDU if(res == _SUCCESS && tx_amsdu == 2) { amsdu_timeout = rtw_amsdu_get_timer_status(padapter, pattrib->priority); if(amsdu_timeout == RTW_AMSDU_TIMER_SETTING) { rtw_amsdu_cancel_timer(padapter, pattrib->priority); rtw_amsdu_set_timer_status(padapter, pattrib->priority, RTW_AMSDU_TIMER_UNSET); } } #endif _exit_critical_bh(&pxmitpriv->lock, &irqL); if (res != _SUCCESS) { RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("pre_xmitframe: enqueue xmitframe fail\n")); rtw_free_xmitframe(pxmitpriv, pxmitframe); pxmitpriv->tx_drop++; return _TRUE; } #ifdef TX_AMSDU tasklet_hi_schedule(&pxmitpriv->xmit_tasklet); #endif return _FALSE; } s32 rtl8822be_mgnt_xmit(_adapter *padapter, struct xmit_frame *pmgntframe) { return rtl8822be_dump_xframe(padapter, pmgntframe); } /* * Return * _TRUE dump packet directly ok * _FALSE temporary can't transmit packets to hardware */ s32 rtl8822be_hal_xmit(_adapter *padapter, struct xmit_frame *pxmitframe) { DBG_COUNTER(padapter->tx_logs.intf_tx); return pre_xmitframe(padapter, pxmitframe); } s32 rtl8822be_hal_xmitframe_enqueue(_adapter *padapter, struct xmit_frame *pxmitframe) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; s32 err; DBG_COUNTER(padapter->tx_logs.intf_tx_enqueue); err = rtw_xmitframe_enqueue(padapter, pxmitframe); if (err != _SUCCESS) { rtw_free_xmitframe(pxmitpriv, pxmitframe); pxmitpriv->tx_drop++; } else { #ifdef PLATFORM_LINUX if (check_nic_enough_desc(padapter, &pxmitframe->attrib) == _TRUE) tasklet_hi_schedule(&pxmitpriv->xmit_tasklet); #endif } return err; } int rtl8822be_init_txbd_ring(_adapter *padapter, unsigned int q_idx, unsigned int entries) { struct xmit_priv *t_priv = &padapter->xmitpriv; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); struct pci_dev *pdev = pdvobjpriv->ppcidev; struct tx_buf_desc *txbd; u8 *tx_desc; dma_addr_t dma; int i; _func_enter_; RTW_INFO("%s entries num:%d\n", __func__, entries); txbd = pci_alloc_consistent(pdev, sizeof(*txbd) * entries, &dma); if (!txbd || (unsigned long)txbd & 0xFF) { RTW_INFO("Cannot allocate TXBD (q_idx = %d)\n", q_idx); return _FAIL; } _rtw_memset(txbd, 0, sizeof(*txbd) * entries); t_priv->tx_ring[q_idx].buf_desc = txbd; t_priv->tx_ring[q_idx].dma = dma; t_priv->tx_ring[q_idx].idx = 0; t_priv->tx_ring[q_idx].entries = entries; _rtw_init_queue(&t_priv->tx_ring[q_idx].queue); t_priv->tx_ring[q_idx].qlen = 0; RTW_INFO("%s queue:%d, ring_addr:%p\n", __func__, q_idx, txbd); _func_exit_; return _SUCCESS; } void rtl8822be_free_txbd_ring(_adapter *padapter, unsigned int prio) { struct xmit_priv *t_priv = &padapter->xmitpriv; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); struct pci_dev *pdev = pdvobjpriv->ppcidev; struct rtw_tx_ring *ring = &t_priv->tx_ring[prio]; u8 *txbd; struct xmit_buf *pxmitbuf; _func_enter_; while (ring->qlen) { txbd = (u8 *)(&ring->buf_desc[ring->idx]); SET_TX_BD_OWN(txbd, 0); if (prio != BCN_QUEUE_INX) ring->idx = (ring->idx + 1) % ring->entries; pxmitbuf = rtl8822be_dequeue_xmitbuf(ring); if (pxmitbuf) { pci_unmap_single(pdev, GET_TX_BD_PHYSICAL_ADDR0_LOW(txbd), pxmitbuf->len, PCI_DMA_TODEVICE); rtw_free_xmitbuf(t_priv, pxmitbuf); } else { RTW_INFO("%s qlen=%d!=0,but have xmitbuf in pendingQ\n", __func__, ring->qlen); break; } } pci_free_consistent(pdev, sizeof(*ring->buf_desc) * ring->entries, ring->buf_desc, ring->dma); ring->buf_desc = NULL; _func_exit_; } /* * Draw a line to show queue status. For debug * i: queue index / W:HW index / h:host index / .: enpty entry / *:ready to DMA * Example: R- 3- 4- 8 ..iW***h..... (i=3,W=4,h=8, * *** means 3 tx_desc is reaady to dma) */ #ifdef BUF_DESC_DEBUG static void _draw_queue(PADAPTER Adapter, int prio) { int i; u8 line[TXBD_NUM + 1]; u16 hw, host; u32 index, tmp_4bytes = 0; struct xmit_priv *t_priv = &Adapter->xmitpriv; struct rtw_tx_ring *ring = &t_priv->tx_ring[prio]; tmp_4bytes = rtw_read32(Adapter, get_txbd_rw_reg(prio)); hw = (u16)((tmp_4bytes >> 16) & 0x7ff); host = (u16)(tmp_4bytes & 0x7ff); index = ring->idx; _rtw_memset(line, '.', TXBD_NUM); /* ready to return to driver */ if (index <= hw) { for (i = index; i < hw; i++) line[i] = ':'; } else { /* wrap */ for (i = index; i < TXBD_NUM; i++) line[i] = ':'; for (i = 0; i < hw; i++) line[i] = ':'; } /* ready to dma */ if (hw <= host) { for (i = hw; i < host; i++) line[i] = '*'; } else { /* wrap */ for (i = hw; i < TXBD_NUM; i++) line[i] = '*'; for (i = 0; i < host; i++) line[i] = '*'; } line[index] = 'i'; /* software queue index */ line[host] = 'h'; /* host index */ line[hw] = 'W'; /* hardware index */ line[TXBD_NUM] = 0x0; /* Q2:10-20-30: */ buf_desc_debug("Q%d:%02d-%02d-%02d %s\n", prio, index, hw, host, line); } #endif /* * Read pointer is h/w descriptor index * Write pointer is host desciptor index: For tx side, if own bit is set in * packet index n, host pointer (write pointer) point to index n + 1. */ static u32 rtl8822be_check_txdesc_closed(PADAPTER Adapter, u32 queue_idx, struct rtw_tx_ring *ring) { /* * hw_rp_cache is used to reduce REG access. */ u32 tmp32; /* bcn queue should not enter this function */ if (queue_idx == BCN_QUEUE_INX) return _TRUE; /* qlen == 0 --> don't need to process */ if (ring->qlen == 0) return _FALSE; /* sw_rp == hw_rp_cache --> sync hw_rp */ if (ring->idx == ring->hw_rp_cache) { tmp32 = rtw_read32(Adapter, get_txbd_rw_reg(queue_idx)); ring->hw_rp_cache = (tmp32 >> 16) & 0x0FFF; } /* check if need to handle TXOK */ if (ring->idx == ring->hw_rp_cache) return _FALSE; return _TRUE; } #ifdef CONFIG_BCN_ICF void rtl8822be_tx_isr(PADAPTER Adapter, int prio) { struct xmit_priv *t_priv = &Adapter->xmitpriv; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(Adapter); struct rtw_tx_ring *ring = &t_priv->tx_ring[prio]; struct xmit_buf *pxmitbuf; u8 *tx_desc; u16 tmp_4bytes; u16 desc_idx_hw = 0, desc_idx_host = 0; while (ring->qlen) { tx_desc = (u8 *)&ring->buf_desc[ring->idx]; /* beacon use cmd buf Never run into here */ if (!rtl8822be_check_txdesc_closed(Adapter, prio, ring)) return; buf_desc_debug("TX: %s, q_idx = %d, tx_bd = %04x, close [%04x] r_idx [%04x]\n", __func__, prio, (u32)tx_desc, ring->idx, (ring->idx + 1) % ring->entries); ring->idx = (ring->idx + 1) % ring->entries; pxmitbuf = rtl8822be_dequeue_xmitbuf(ring); if (pxmitbuf) { pci_unmap_single(pdvobjpriv->ppcidev, GET_TX_BD_PHYSICAL_ADDR0_LOW(tx_desc), pxmitbuf->len, PCI_DMA_TODEVICE); rtw_sctx_done(&pxmitbuf->sctx); rtw_free_xmitbuf(&(pxmitbuf->padapter->xmitpriv), pxmitbuf); } else { RTW_INFO("%s qlen=%d!=0,but have xmitbuf in pendingQ\n", __func__, ring->qlen); } } if (check_tx_desc_resource(Adapter, prio) && rtw_xmit_ac_blocked(Adapter) != _TRUE) rtw_mi_xmit_tasklet_schedule(Adapter); } #else /* !CONFIG_BCN_ICF */ void rtl8822be_tx_isr(PADAPTER Adapter, int prio) { struct xmit_priv *t_priv = &Adapter->xmitpriv; struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(Adapter); struct rtw_tx_ring *ring = &t_priv->tx_ring[prio]; struct xmit_buf *pxmitbuf; u8 *tx_desc; u16 tmp_4bytes; u16 desc_idx_hw = 0, desc_idx_host = 0; while (ring->qlen) { tx_desc = (u8 *)&ring->buf_desc[ring->idx]; /* * beacon packet will only use the first descriptor defautly, * check register to see whether h/w has consumed buffer * descriptor */ if (prio != BCN_QUEUE_INX) { if (!rtl8822be_check_txdesc_closed(Adapter, prio, ring->idx)) return; buf_desc_debug("TX: %s, queue_idx = %d, tx_desc = %04x, close desc [%04x] and update ring->idx to [%04x]\n", __func__, prio, (u32)tx_desc, ring->idx, (ring->idx + 1) % ring->entries); ring->idx = (ring->idx + 1) % ring->entries; } #if 0 /* 8822b change 00[31] to DISQSELSEQ */ else if (prio == BCN_QUEUE_INX) SET_TX_DESC_OWN_92E(tx_desc, 0); #endif pxmitbuf = rtl8822be_dequeue_xmitbuf(ring); if (pxmitbuf) { pci_unmap_single(pdvobjpriv->ppcidev, GET_TX_BD_PHYSICAL_ADDR0_LOW(tx_desc), pxmitbuf->len, PCI_DMA_TODEVICE); rtw_sctx_done(&pxmitbuf->sctx); rtw_free_xmitbuf(&(pxmitbuf->padapter->xmitpriv), pxmitbuf); } else { RTW_INFO("%s qlen=%d!=0,but have xmitbuf in pendingQ\n", __func__, ring->qlen); } } if ((prio != BCN_QUEUE_INX) && check_tx_desc_resource(Adapter, prio) && rtw_xmit_ac_blocked(Adapter) != _TRUE) rtw_mi_xmit_tasklet_schedule(Adapter); } #endif /* CONFIG_BCN_ICF */ #ifdef CONFIG_HOSTAPD_MLME static void rtl8812ae_hostap_mgnt_xmit_cb(struct urb *urb) { #ifdef PLATFORM_LINUX struct sk_buff *skb = (struct sk_buff *)urb->context; dev_kfree_skb_any(skb); #endif } s32 rtl8822be_hostap_mgnt_xmit_entry(_adapter *padapter, _pkt *pkt) { #ifdef PLATFORM_LINUX u16 fc; int rc, len, pipe; unsigned int bmcst, tid, qsel; struct sk_buff *skb, *pxmit_skb; struct urb *urb; unsigned char *pxmitbuf; struct tx_desc *ptxdesc; struct rtw_ieee80211_hdr *tx_hdr; struct hostapd_priv *phostapdpriv = padapter->phostapdpriv; struct net_device *pnetdev = padapter->pnetdev; HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter); struct dvobj_priv *pdvobj = adapter_to_dvobj(padapter); skb = pkt; len = skb->len; tx_hdr = (struct rtw_ieee80211_hdr *)(skb->data); fc = le16_to_cpu(tx_hdr->frame_ctl); bmcst = IS_MCAST(tx_hdr->addr1); if ((fc & RTW_IEEE80211_FCTL_FTYPE) != RTW_IEEE80211_FTYPE_MGMT) goto _exit; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) /* http://www.mail-archive.com/netdev@vger.kernel.org/msg17214.html */ pxmit_skb = dev_alloc_skb(len + TXDESC_SIZE); #else pxmit_skb = netdev_alloc_skb(pnetdev, len + TXDESC_SIZE); #endif if (!pxmit_skb) goto _exit; pxmitbuf = pxmit_skb->data; urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) goto _exit; /* ----- fill tx desc ----- */ ptxdesc = (struct tx_desc *)pxmitbuf; _rtw_memset(ptxdesc, 0, sizeof(*ptxdesc)); /* offset 0 */ ptxdesc->txdw0 |= cpu_to_le32(len & 0x0000ffff); /* default = 32 bytes for TX Desc */ ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + OFFSET_SZ) << OFFSET_SHT) & 0x00ff0000); ptxdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG); if (bmcst) ptxdesc->txdw0 |= cpu_to_le32(BIT(24)); /* offset 4 */ ptxdesc->txdw1 |= cpu_to_le32(0x00);/* MAC_ID */ ptxdesc->txdw1 |= cpu_to_le32((0x12 << QSEL_SHT) & 0x00001f00); ptxdesc->txdw1 |= cpu_to_le32((0x06 << 16) & 0x000f0000);/* b mode */ /* offset 8 */ /* offset 12 */ ptxdesc->txdw3 |= cpu_to_le32((le16_to_cpu(tx_hdr->seq_ctl) << 16) & 0xffff0000); /* offset 16 */ ptxdesc->txdw4 |= cpu_to_le32(BIT(8));/* driver uses rate */ /* offset 20 */ rtl8188e_cal_txdesc_chksum(ptxdesc); /* ----- end of fill tx desc ----- */ skb_put(pxmit_skb, len + TXDESC_SIZE); pxmitbuf = pxmitbuf + TXDESC_SIZE; _rtw_memcpy(pxmitbuf, skb->data, len); /* ----- prepare urb for submit ----- */ /* translate DMA FIFO addr to pipehandle */ /*pipe = ffaddr2pipehdl(pdvobj, MGT_QUEUE_INX);*/ pipe = usb_sndbulkpipe(pdvobj->pusbdev, pHalData->Queue2EPNum[(u8)MGT_QUEUE_INX] & 0x0f); usb_fill_bulk_urb(urb, pdvobj->pusbdev, pipe, pxmit_skb->data, pxmit_skb->len, rtl8188ee_hostap_mgnt_xmit_cb, pxmit_skb); urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(urb, &phostapdpriv->anchored); rc = usb_submit_urb(urb, GFP_ATOMIC); if (rc < 0) { usb_unanchor_urb(urb); kfree_skb(skb); } usb_free_urb(urb); _exit: dev_kfree_skb_any(skb); #endif return 0; } #endif