summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/rtl8192ce/hal/rtl8192c/pci/rtl8192ce_xmit.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rtl8192ce/hal/rtl8192c/pci/rtl8192ce_xmit.c')
-rwxr-xr-xdrivers/net/wireless/rtl8192ce/hal/rtl8192c/pci/rtl8192ce_xmit.c975
1 files changed, 975 insertions, 0 deletions
diff --git a/drivers/net/wireless/rtl8192ce/hal/rtl8192c/pci/rtl8192ce_xmit.c b/drivers/net/wireless/rtl8192ce/hal/rtl8192c/pci/rtl8192ce_xmit.c
new file mode 100755
index 000000000000..fa057d1b3fbf
--- /dev/null
+++ b/drivers/net/wireless/rtl8192ce/hal/rtl8192c/pci/rtl8192ce_xmit.c
@@ -0,0 +1,975 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 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 _RTL8192C_XMIT_C_
+#include <drv_conf.h>
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtw_byteorder.h>
+#include <wifi.h>
+#include <osdep_intf.h>
+#include <circ_buf.h>
+#include <pci_ops.h>
+#include <rtl8192c_hal.h>
+
+#if defined (PLATFORM_LINUX) && defined (PLATFORM_WINDOWS)
+#error "Shall be Linux or Windows, but not both!\n"
+#endif
+
+
+s32 rtl8192ce_init_xmit_priv(_adapter *padapter)
+{
+ s32 ret = _SUCCESS;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct dvobj_priv *pdvobjpriv = &padapter->dvobjpriv;
+ HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter);
+
+ _rtw_spinlock_init(&pdvobjpriv->irq_th_lock);
+ _rtw_spinlock_init(&pHalData->rf_lock);
+
+#ifdef PLATFORM_LINUX
+ tasklet_init(&pxmitpriv->xmit_tasklet,
+ (void(*)(unsigned long))rtl8192ce_xmit_tasklet,
+ (unsigned long)padapter);
+#endif
+
+ return ret;
+}
+
+void rtl8192ce_free_xmit_priv(_adapter *padapter)
+{
+ //u8 i;
+ struct dvobj_priv *pdvobjpriv = &padapter->dvobjpriv;
+ HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter);
+
+ _rtw_spinlock_free(&pdvobjpriv->irq_th_lock);
+ _rtw_spinlock_free(&pHalData->rf_lock);
+
+}
+
+s32 rtl8192ce_enqueue_xmitbuf(struct rtw_tx_ring *ring, struct xmit_buf *pxmitbuf)
+{
+ _irqL irqL;
+ _queue *ppending_queue = &ring->queue;
+
+_func_enter_;
+
+ //DBG_8192C("+enqueue_xmitbuf\n");
+
+ if(pxmitbuf==NULL)
+ {
+ return _FAIL;
+ }
+
+ //_enter_critical(&ppending_queue->lock, &irqL);
+
+ rtw_list_delete(&pxmitbuf->list);
+
+ rtw_list_insert_tail(&(pxmitbuf->list), get_list_head(ppending_queue));
+
+ ring->qlen++;
+
+ //DBG_8192C("FREE, free_xmitbuf_cnt=%d\n", pxmitpriv->free_xmitbuf_cnt);
+
+ //_exit_critical(&ppending_queue->lock, &irqL);
+
+_func_exit_;
+
+ return _SUCCESS;
+}
+
+struct xmit_buf * rtl8192ce_dequeue_xmitbuf(struct rtw_tx_ring *ring)
+{
+ _irqL irqL;
+ _list *plist, *phead;
+ struct xmit_buf *pxmitbuf = NULL;
+ _queue *ppending_queue = &ring->queue;
+
+_func_enter_;
+
+ //_enter_critical(&ppending_queue->lock, &irqL);
+
+ 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--;
+
+ //_exit_critical(&ppending_queue->lock, &irqL);
+
+_func_exit_;
+
+ return pxmitbuf;
+}
+
+static u32 rtw_get_ff_hwaddr(struct xmit_frame *pxmitframe)
+{
+ u32 addr;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+ switch(pattrib->qsel)
+ {
+ case 0:
+ case 3:
+ addr = BE_QUEUE_INX;
+ break;
+ case 1:
+ case 2:
+ addr = BK_QUEUE_INX;
+ break;
+ case 4:
+ case 5:
+ addr = VI_QUEUE_INX;
+ break;
+ case 6:
+ case 7:
+ addr = VO_QUEUE_INX;
+ break;
+ case 0x10:
+ addr = BCN_QUEUE_INX;
+ break;
+ case 0x11://BC/MC in PS (HIQ)
+ addr = HIGH_QUEUE_INX;
+ break;
+ case 0x12:
+ addr = MGT_QUEUE_INX;
+ break;
+ default:
+ addr = BE_QUEUE_INX;
+ break;
+
+ }
+
+ return addr;
+
+}
+
+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;
+}
+
+
+static void fill_txdesc_sectype(struct pkt_attrib *pattrib, struct tx_desc *ptxdesc)
+{
+ if ((pattrib->encrypt > 0) && !pattrib->bswenc)
+ {
+ switch (pattrib->encrypt)
+ {
+ //SEC_TYPE
+ case _WEP40_:
+ case _WEP104_:
+ ptxdesc->txdw1 |= cpu_to_le32((0x01<<22)&0x00c00000);
+ break;
+ case _TKIP_:
+ case _TKIP_WTMIC_:
+ //ptxdesc->txdw1 |= cpu_to_le32((0x02<<22)&0x00c00000);
+ ptxdesc->txdw1 |= cpu_to_le32((0x01<<22)&0x00c00000);
+ break;
+ case _AES_:
+ ptxdesc->txdw1 |= cpu_to_le32((0x03<<22)&0x00c00000);
+ break;
+ case _NO_PRIVACY_:
+ default:
+ break;
+
+ }
+
+ }
+
+}
+
+static void fill_txdesc_vcs(struct pkt_attrib *pattrib, u32 *pdw)
+{
+ //DBG_8192C("cvs_mode=%d\n", pattrib->vcs_mode);
+
+ switch(pattrib->vcs_mode)
+ {
+ case RTS_CTS:
+ *pdw |= cpu_to_le32(BIT(12));
+ break;
+ case CTS_TO_SELF:
+ *pdw |= cpu_to_le32(BIT(11));
+ break;
+ case NONE_VCS:
+ default:
+ break;
+ }
+
+ if(pattrib->vcs_mode) {
+ *pdw |= cpu_to_le32(BIT(13));
+
+ // Set RTS BW
+ if(pattrib->ht_en)
+ {
+ *pdw |= (pattrib->bwmode&HT_CHANNEL_WIDTH_40)? cpu_to_le32(BIT(27)):0;
+
+ if(pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ *pdw |= cpu_to_le32((0x01<<28)&0x30000000);
+ else if(pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ *pdw |= cpu_to_le32((0x02<<28)&0x30000000);
+ else if(pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)
+ *pdw |= 0;
+ else
+ *pdw |= cpu_to_le32((0x03<<28)&0x30000000);
+ }
+ }
+}
+
+static void fill_txdesc_phy(struct pkt_attrib *pattrib, u32 *pdw)
+{
+ //DBG_8192C("bwmode=%d, ch_off=%d\n", pattrib->bwmode, pattrib->ch_offset);
+
+ if(pattrib->ht_en)
+ {
+ *pdw |= (pattrib->bwmode&HT_CHANNEL_WIDTH_40)? cpu_to_le32(BIT(25)):0;
+
+ if(pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ *pdw |= cpu_to_le32((0x01<<20)&0x00300000);
+ else if(pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ *pdw |= cpu_to_le32((0x02<<20)&0x00300000);
+ else if(pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)
+ *pdw |= 0;
+ else
+ *pdw |= cpu_to_le32((0x03<<20)&0x00300000);
+ }
+}
+
+static s32 update_txdesc(struct xmit_frame *pxmitframe, u32 *pmem, s32 sz)
+{
+ uint qsel;
+ _adapter *padapter = pxmitframe->padapter;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct dvobj_priv *pdvobjpriv = &padapter->dvobjpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ struct mlme_ext_info *pmlmeinfo = &padapter->mlmeextpriv.mlmext_info;
+ HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ struct tx_desc *ptxdesc = (struct tx_desc *)pmem;
+ sint bmcst = IS_MCAST(pattrib->ra);
+#ifdef CONFIG_P2P
+ struct wifidirect_info* pwdinfo = &padapter->wdinfo;
+#endif //CONFIG_P2P
+
+ dma_addr_t mapping = pci_map_single(pdvobjpriv->ppcidev, pxmitframe->buf_addr , sz, PCI_DMA_TODEVICE);
+
+ _rtw_memset(ptxdesc, 0, TX_DESC_NEXT_DESC_OFFSET);
+
+ if((pxmitframe->frame_tag&0x0f) == DATA_FRAMETAG)
+ {
+ //DBG_8192C("pxmitframe->frame_tag == DATA_FRAMETAG\n");
+
+ //offset 4
+ ptxdesc->txdw1 |= cpu_to_le32((pattrib->mac_id)&0x1f);
+
+ qsel = (uint)(pattrib->qsel & 0x0000001f);
+ ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00);
+
+ ptxdesc->txdw1 |= cpu_to_le32((pattrib->raid<< 16) & 0x000f0000);
+
+ fill_txdesc_sectype(pattrib, ptxdesc);
+
+ if(pattrib->ampdu_en==_TRUE)
+ ptxdesc->txdw1 |= cpu_to_le32(BIT(5));//AGG EN
+ else
+ ptxdesc->txdw1 |= cpu_to_le32(BIT(6));//AGG BK
+
+ //offset 8
+
+
+ //offset 12
+ ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<16)&0xffff0000);
+
+
+ //offset 16 , offset 20
+ if (pattrib->qos_en)
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(6));//QoS
+
+ if ((pattrib->ether_type != 0x888e) && (pattrib->ether_type != 0x0806) && (pattrib->dhcp_pkt != 1))
+ {
+ //Non EAP & ARP & DHCP type data packet
+
+ fill_txdesc_vcs(pattrib, &ptxdesc->txdw4);
+ fill_txdesc_phy(pattrib, &ptxdesc->txdw4);
+
+ ptxdesc->txdw4 |= cpu_to_le32(0x00000008);//RTS Rate=24M
+ ptxdesc->txdw5 |= cpu_to_le32(0x0001ff00);//
+ //ptxdesc->txdw5 |= cpu_to_le32(0x0000000b);//DataRate - 54M
+
+ //use REG_INIDATA_RATE_SEL value
+ ptxdesc->txdw5 |= cpu_to_le32(pdmpriv->INIDATA_RATE[pattrib->mac_id]);
+
+ if(0)//for driver dbg
+ {
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(8));//driver uses rate
+
+ if(pattrib->ht_en)
+ ptxdesc->txdw5 |= cpu_to_le32(BIT(6));//SGI
+
+ ptxdesc->txdw5 |= cpu_to_le32(0x00000013);//init rate - mcs7
+ }
+ }
+ else
+ {
+ // EAP data packet and ARP packet.
+ // Use the 1M data rate to send the EAP/ARP packet.
+ // This will maybe make the handshake smooth.
+
+ ptxdesc->txdw1 |= cpu_to_le32(BIT(6));//AGG BK
+
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(8));//driver uses rate
+
+ if (pmlmeinfo->preamble_mode == PREAMBLE_SHORT)
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(24));// DATA_SHORT
+ }
+
+ //offset 24
+
+#ifdef CONFIG_TCP_CSUM_OFFLOAD_TX
+ if ( pattrib->hw_tcp_csum == 1 ) {
+ // ptxdesc->txdw6 = 0; // clear TCP_CHECKSUM and IP_CHECKSUM. It's zero already!!
+ u8 ip_hdr_offset = 32 + pattrib->hdrlen + pattrib->iv_len + 8;
+ ptxdesc->txdw7 = (1 << 31) | (ip_hdr_offset << 16);
+ DBG_8192C("ptxdesc->txdw7 = %08x\n", ptxdesc->txdw7);
+ }
+#endif
+ }
+ else if((pxmitframe->frame_tag&0x0f)== MGNT_FRAMETAG)
+ {
+ //DBG_8192C("pxmitframe->frame_tag == MGNT_FRAMETAG\n");
+
+ //offset 4
+ ptxdesc->txdw1 |= cpu_to_le32((pattrib->mac_id)&0x1f);
+
+ qsel = (uint)(pattrib->qsel&0x0000001f);
+ ptxdesc->txdw1 |= cpu_to_le32((qsel<<QSEL_SHT)&0x00001f00);
+
+ ptxdesc->txdw1 |= cpu_to_le32((pattrib->raid<< 16) & 0x000f0000);
+
+ //fill_txdesc_sectype(pattrib, ptxdesc);
+
+ //offset 8
+
+ //offset 12
+ ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<16)&0xffff0000);
+
+ //offset 16
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(8));//driver uses rate
+
+ //offset 20
+
+ }
+ else if((pxmitframe->frame_tag&0x0f) == TXAGG_FRAMETAG)
+ {
+ DBG_8192C("pxmitframe->frame_tag == TXAGG_FRAMETAG\n");
+ }
+#ifdef CONFIG_MP_INCLUDED
+ else if((pxmitframe->frame_tag&0x0f) == MP_FRAMETAG)
+ {
+ fill_txdesc_for_mp(padapter, ptxdesc);
+ }
+#endif
+ else
+ {
+ DBG_8192C("pxmitframe->frame_tag = %d\n", pxmitframe->frame_tag);
+
+ //offset 4
+ ptxdesc->txdw1 |= cpu_to_le32((4)&0x1f);//CAM_ID(MAC_ID)
+
+ ptxdesc->txdw1 |= cpu_to_le32((6<< 16) & 0x000f0000);//raid
+
+ //offset 8
+
+ //offset 12
+ ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<16)&0xffff0000);
+
+ //offset 16
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(8));//driver uses rate
+
+ //offset 20
+ ptxdesc->txdw5 |= cpu_to_le32(BIT(17));//retry limit enable
+ ptxdesc->txdw5 |= cpu_to_le32(0x00180000);//retry limit = 6
+#ifdef CONFIG_P2P
+ // Added by Albert 2011/03/17
+ // In the P2P mode, the driver should not support the b mode.
+ // So, the Tx packet shouldn't use the CCK rate
+ if(!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE))
+ {
+ ptxdesc->txdw5 |= cpu_to_le32( 0x04 ); // Use the 6M data rate.
+ }
+#endif //CONFIG_P2P
+
+ }
+
+ //offset 28
+ ptxdesc->txdw7 |= cpu_to_le32(sz&0x0000ffff);
+
+ //offset 32
+ ptxdesc->txdw8 = cpu_to_le32(mapping);
+
+ // 2009.11.05. tynli_test. Suggested by SD4 Filen for FW LPS.
+ // (1) The sequence number of each non-Qos frame / broadcast / multicast /
+ // mgnt frame should be controled by Hw because Fw will also send null data
+ // which we cannot control when Fw LPS enable.
+ // --> default enable non-Qos data sequense number. 2010.06.23. by tynli.
+ // (2) Enable HW SEQ control for beacon packet, because we use Hw beacon.
+ // (3) Use HW Qos SEQ to control the seq num of Ext port non-Qos packets.
+ // 2010.06.23. Added by tynli.
+ if(!pattrib->qos_en)
+ {
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(7)); // Hw set sequence number
+ ptxdesc->txdw3 |= cpu_to_le32((8 <<28)); //set bit3 to 1. Suugested by TimChen. 2009.12.29.
+ }
+
+ //offset 0
+ if(bmcst)
+ {
+ ptxdesc->txdw0 |= cpu_to_le32(BIT(24));
+ }
+ ptxdesc->txdw0 |= cpu_to_le32(sz&0x0000ffff);
+ ptxdesc->txdw0 |= cpu_to_le32(FSG | LSG);
+ ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE+OFFSET_SZ)<<OFFSET_SHT)&0x00ff0000);//32 bytes for TX Desc
+// ptxdesc->txdw0 |= cpu_to_le32(OWN);
+
+ RT_TRACE(_module_rtl871x_xmit_c_,_drv_info_,("offset0-txdesc=0x%x\n", ptxdesc->txdw0));
+
+ return 0;
+
+}
+
+
+static struct tx_desc *get_txdesc(_adapter *padapter, u8 queue_index)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct rtw_tx_ring *ring;
+ struct tx_desc *pdesc = NULL;
+ int idx;
+
+ ring = &pxmitpriv->tx_ring[queue_index];
+ if (queue_index != BCN_QUEUE_INX) {
+ idx = (ring->idx + ring->qlen) % ring->entries;
+ } else {
+ idx = 0;
+ }
+
+ pdesc = &ring->desc[idx];
+ if((le32_to_cpu(pdesc->txdw0) & OWN) && (queue_index != BCN_QUEUE_INX)) {
+ DBG_8192C("No more TX desc@%d, ring->idx = %d,idx = %d \n", queue_index, ring->idx, idx);
+ return NULL;
+ }
+
+ return pdesc;
+}
+
+
+void rtw_dump_xframe(_adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ _irqL irqL;
+ int t, sz, w_sz, pull=0;
+ //u8 *mem_addr;
+ 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 = &padapter->dvobjpriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct tx_desc *ptxdesc;
+
+ 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);
+ }
+
+ //mem_addr = pxmitframe->buf_addr;
+
+ RT_TRACE(_module_rtl871x_xmit_c_,_drv_info_,("rtw_dump_xframe()\n"));
+
+ for (t = 0; t < pattrib->nr_frags; t++)
+ {
+ 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;
+ sz = sz - 4 - (psecuritypriv->sw_encrypt ? 0 : pattrib->icv_len);
+ }
+ else //no frag
+ {
+ sz = pattrib->last_txcmdsz;
+ }
+
+ ff_hwaddr = rtw_get_ff_hwaddr(pxmitframe);
+
+#ifdef CONFIG_CONCURRENT_MODE
+ if(!rtw_buddy_adapter_up(padapter))
+ goto skip_if2_tx;
+ if(padapter->adapter_type > PRIMARY_ADAPTER)
+ {
+ _adapter *pri_adapter = padapter->pbuddy_adapter;
+ struct dvobj_priv *pri_dvobjpriv = &pri_adapter->dvobjpriv;
+
+ _enter_critical(&(pri_dvobjpriv->irq_th_lock), &irqL);
+
+
+
+
+ ptxdesc = get_txdesc(pri_adapter, ff_hwaddr);
+
+ if(ptxdesc == NULL)
+ {
+ _exit_critical(&pri_dvobjpriv->irq_th_lock, &irqL);
+ rtw_free_xmitbuf(pxmitpriv, pxmitbuf);
+ DBG_8192C("##### Tx desc unavailable !#####\n");
+ break;
+ }
+ update_txdesc(pxmitframe, (uint*)ptxdesc, sz);
+
+ rtl8192ce_enqueue_xmitbuf(&(pri_adapter->xmitpriv.tx_ring[ff_hwaddr]), pxmitbuf);
+ pxmitbuf->len = sz;
+
+ w_sz = sz;
+
+ wmb();
+ ptxdesc->txdw0 |= cpu_to_le32(OWN);
+ _exit_critical(&pri_dvobjpriv->irq_th_lock, &irqL);
+
+ rtw_write16(pri_adapter, REG_PCIE_CTRL_REG, ffaddr2dma(ff_hwaddr));
+ rtw_write_port(padapter, ff_hwaddr, w_sz, (unsigned char*)pxmitbuf);
+
+ } else
+skip_if2_tx:
+#endif
+ {
+ _enter_critical(&pdvobjpriv->irq_th_lock, &irqL);
+
+ ptxdesc = get_txdesc(pxmitframe->padapter, ff_hwaddr);
+
+ if(ptxdesc == NULL)
+ {
+ _exit_critical(&pdvobjpriv->irq_th_lock, &irqL);
+ rtw_free_xmitbuf(pxmitpriv, pxmitbuf);
+ DBG_8192C("##### Tx desc unavailable !#####\n");
+ break;
+ }
+
+ update_txdesc(pxmitframe, (uint*)ptxdesc, sz);
+
+
+ rtl8192ce_enqueue_xmitbuf(&pxmitpriv->tx_ring[ff_hwaddr], pxmitbuf);
+
+ pxmitbuf->len = sz;
+ w_sz = sz;
+
+ wmb();
+ ptxdesc->txdw0 |= cpu_to_le32(OWN);
+
+ _exit_critical(&pdvobjpriv->irq_th_lock, &irqL);
+ rtw_write16(padapter, REG_PCIE_CTRL_REG, ffaddr2dma(ff_hwaddr));
+ rtw_write_port(padapter, ff_hwaddr, w_sz, (unsigned char*)pxmitbuf);
+
+ }
+
+ rtw_count_tx_stats(padapter, pxmitframe, sz);
+
+ RT_TRACE(_module_rtl871x_xmit_c_,_drv_info_,("rtw_write_port, w_sz=%d\n", w_sz));
+ //DBG_8192C("rtw_write_port, w_sz=%d, sz=%d, txdesc_sz=%d, tid=%d\n", w_sz, sz, w_sz-sz, pattrib->priority);
+
+ //mem_addr += w_sz;
+
+ //mem_addr = (u8 *)RND4(((SIZE_PTR)(mem_addr)));
+
+ }
+
+ rtw_free_xmitframe_ex(pxmitpriv, pxmitframe);
+
+}
+
+void rtl8192ce_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;
+
+ RT_TRACE(_module_rtl871x_xmit_c_,_drv_info_,("rtl8192ce_xmitframe_resume()\n"));
+
+ while(1)
+ {
+ if ((padapter->bDriverStopped == _TRUE)||(padapter->bSurpriseRemoved== _TRUE))
+ {
+ DBG_8192C("rtl8192ce_xmitframe_resume => bDriverStopped or bSurpriseRemoved \n");
+ break;
+ }
+
+ pxmitbuf = rtw_alloc_xmitbuf(pxmitpriv);
+ if(!pxmitbuf)
+ {
+ break;
+ }
+
+ 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);
+ }
+
+ rtw_os_xmit_complete(padapter, pxmitframe);//always return ndis_packet after rtw_xmitframe_coalesce
+ }
+
+ RT_TRACE(_module_rtl871x_xmit_c_,_drv_info_,("rtl8192ce_xmitframe_resume(): rtw_dump_xframe\n"));
+
+ if(res == _SUCCESS)
+ {
+ rtw_dump_xframe(padapter, pxmitframe);
+ }
+ else
+ {
+ rtw_free_xmitbuf(pxmitpriv, pxmitbuf);
+ rtw_free_xmitframe_ex(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 {
+ DBG_8192C("do not have enough desc for Tx \n");
+ return _FALSE;
+ }
+}
+
+static s32 xmitframe_direct(_adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ s32 res = _SUCCESS;
+
+ res = rtw_xmitframe_coalesce(padapter, pxmitframe->pkt, pxmitframe);
+ if (res == _SUCCESS) {
+ rtw_dump_xframe(padapter, pxmitframe);
+ }
+
+ return res;
+}
+
+/*
+ * 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 CONFIG_CONCURRENT_MODE
+ PADAPTER pbuddy_adapter = padapter->pbuddy_adapter;
+ struct mlme_priv *pbuddy_mlmepriv = &(pbuddy_adapter->mlmepriv);
+#endif // CONFIG_CONCURRENT_MODE
+
+ _enter_critical_bh(&pxmitpriv->lock, &irqL);
+
+ if ( (rtw_txframes_sta_ac_pending(padapter, pattrib) > 0) ||
+ (check_nic_enough_desc(padapter, pattrib) == _FALSE))
+ goto enqueue;
+
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == _TRUE)
+ goto enqueue;
+
+#ifdef CONFIG_CONCURRENT_MODE
+ if (check_fwstate(pbuddy_mlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING) == _TRUE)
+ goto enqueue;
+#endif // CONFIG_CONCURRENT_MODE
+ pxmitbuf = rtw_alloc_xmitbuf(pxmitpriv);
+ if (pxmitbuf == NULL)
+ 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_ex(pxmitpriv, pxmitframe);
+ }
+
+ return _TRUE;
+
+enqueue:
+ res = rtw_xmitframe_enqueue(padapter, pxmitframe);
+ _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_ex(pxmitpriv, pxmitframe);
+
+ // Trick, make the statistics correct
+ pxmitpriv->tx_pkts--;
+ pxmitpriv->tx_drop++;
+ return _TRUE;
+ }
+
+ return _FALSE;
+}
+
+void rtl8192ce_mgnt_xmit(_adapter *padapter, struct xmit_frame *pmgntframe)
+{
+ rtw_dump_xframe(padapter, pmgntframe);
+}
+
+/*
+ * Return
+ * _TRUE dump packet directly ok
+ * _FALSE temporary can't transmit packets to hardware
+ */
+s32 rtl8192ce_hal_xmit(_adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ return pre_xmitframe(padapter, pxmitframe);
+}
+
+#ifdef CONFIG_HOSTAPD_MLME
+
+static void rtl8192ce_hostap_mgnt_xmit_cb(struct urb *urb)
+{
+#ifdef PLATFORM_LINUX
+ struct sk_buff *skb = (struct sk_buff *)urb->context;
+
+ //DBG_8192C("%s\n", __FUNCTION__);
+
+ dev_kfree_skb_any(skb);
+#endif
+}
+
+s32 rtl8192ce_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 = &padapter->dvobjpriv;
+
+
+ //DBG_8192C("%s\n", __FUNCTION__);
+
+ 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);
+ ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE+OFFSET_SZ)<<OFFSET_SHT)&0x00ff0000);//default = 32 bytes for TX Desc
+ 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
+
+ rtl8192cu_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);
+
+ //DBG_8192C("mgnt_xmit, len=%x\n", pxmit_skb->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, rtl8192cu_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
+