/****************************************************************************** * * 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. * ******************************************************************************/ #define _RTL8192C_XMIT_C_ #include #include #include #include #include /* include */ #include static int urb_zero_packet_chk(struct rtw_adapter *padapter, int sz) { int blnSetTxDescOffset; struct dvobj_priv *pdvobj = adapter_to_dvobj(padapter); if (pdvobj->ishighspeed) { if (((sz + TXDESC_SIZE) % 512) == 0) blnSetTxDescOffset = 1; else blnSetTxDescOffset = 0; } else { if (((sz + TXDESC_SIZE) % 64) == 0) blnSetTxDescOffset = 1; else blnSetTxDescOffset = 0; } return blnSetTxDescOffset; } static void rtl8192cu_cal_txdesc_chksum(struct tx_desc *ptxdesc) { __le16 *usPtr = (__le16 *)ptxdesc; u32 count = 16; /* (32 bytes / 2 bytes per XOR) => 16 times */ u32 index; u16 checksum = 0; /* Clear first */ ptxdesc->txdw7 &= cpu_to_le32(0xffff0000); for (index = 0 ; index < count ; index++) checksum = checksum ^ le16_to_cpu(*(usPtr + index)); ptxdesc->txdw7 |= cpu_to_le32(0x0000ffff&checksum); } 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 WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: ptxdesc->txdw1 |= cpu_to_le32((0x01<<22)&0x00c00000); break; case WLAN_CIPHER_SUITE_TKIP: /* ptxdesc->txdw1 |= cpu_to_le32((0x02<<22)&0x00c00000); */ ptxdesc->txdw1 |= cpu_to_le32((0x01<<22)&0x00c00000); break; case WLAN_CIPHER_SUITE_CCMP: ptxdesc->txdw1 |= cpu_to_le32((0x03<<22)&0x00c00000); break; case 0: default: break; } } } static void fill_txdesc_vcs(struct pkt_attrib *pattrib, __le32 *pdw) { /* DBG_8723A("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, __le32 *pdw) { 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)&0x003f0000); else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER) *pdw |= cpu_to_le32((0x02<<20)&0x003f0000); else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE) *pdw |= 0; else *pdw |= cpu_to_le32((0x03<<20)&0x003f0000); } } static s32 update_txdesc(struct xmit_frame *pxmitframe, u8 *pmem, s32 sz) { int pull = 0; uint qsel; struct rtw_adapter *padapter = pxmitframe->padapter; struct pkt_attrib *pattrib = &pxmitframe->attrib; struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter); struct dm_priv *pdmpriv = &pHalData->dmpriv; struct tx_desc *ptxdesc = (struct tx_desc *)pmem; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; int bmcst = is_multicast_ether_addr(pattrib->ra); if (urb_zero_packet_chk(padapter, sz) == 0) { ptxdesc = (struct tx_desc *)(pmem+PACKET_OFFSET_SZ); pull = 1; pxmitframe->pkt_offset--; } memset(ptxdesc, 0, sizeof(struct tx_desc)); if (pxmitframe->frame_tag == DATA_FRAMETAG) { /* 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) 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);/* */ /* use REG_INIDATA_RATE_SEL value */ ptxdesc->txdw5 |= cpu_to_le32(pdmpriv->INIDATA_RATE[pattrib->mac_id]); } 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 */ ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate23a(pmlmeext->tx_rate)); } } else if (pxmitframe->frame_tag == MGNT_FRAMETAG) { /* offset 4 */ ptxdesc->txdw1 |= cpu_to_le32(pattrib->mac_id&0x1f); qsel = (uint)(pattrib->qsel&0x0000001f); ptxdesc->txdw1 |= cpu_to_le32((qsel<txdw1 |= cpu_to_le32((pattrib->raid<<16) & 0x000f0000); /* offset 8 */ /* CCX-TXRPT ack for xmit mgmt frames. */ if (pxmitframe->ack_report) ptxdesc->txdw2 |= cpu_to_le32(BIT(19)); /* 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 */ ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate23a(pmlmeext->tx_rate)); } else if (pxmitframe->frame_tag == TXAGG_FRAMETAG) { DBG_8723A("pxmitframe->frame_tag == TXAGG_FRAMETAG\n"); } else { DBG_8723A("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(MRateToHwRate23a(pmlmeext->tx_rate)); } /* (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. */ if (!pattrib->qos_en) { /* Hw set sequence number */ ptxdesc->txdw4 |= cpu_to_le32(BIT(7)); /* set bit3 to 1. */ ptxdesc->txdw3 |= cpu_to_le32((8 << 28)); } /* offset 0 */ ptxdesc->txdw0 |= cpu_to_le32(sz&0x0000ffff); ptxdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG); ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE+OFFSET_SZ)<txdw0 |= cpu_to_le32(BIT(24)); RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, "offset0-txdesc = 0x%x\n", ptxdesc->txdw0); /* offset 4 */ /* pkt_offset, unit:8 bytes padding */ if (pxmitframe->pkt_offset > 0) ptxdesc->txdw1 |= cpu_to_le32((pxmitframe->pkt_offset << 26) & 0x7c000000); rtl8192cu_cal_txdesc_chksum(ptxdesc); return pull; } static int rtw_dump_xframe(struct rtw_adapter *padapter, struct xmit_frame *pxmitframe) { int ret = _SUCCESS; int inner_ret = _SUCCESS; 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; if (pxmitframe->frame_tag == DATA_FRAMETAG && pxmitframe->attrib.ether_type != ETH_P_ARP && pxmitframe->attrib.ether_type != ETH_P_PAE && pxmitframe->attrib.dhcp_pkt != 1) rtw_issue_addbareq_cmd23a(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 (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; sz = sz - 4 - pattrib->icv_len; } else { /* no frag */ sz = pattrib->last_txcmdsz; } pull = update_txdesc(pxmitframe, mem_addr, sz); if (pull) { mem_addr += PACKET_OFFSET_SZ; /* pull txdesc head */ pxmitframe->buf_addr = mem_addr; w_sz = sz + TXDESC_SIZE; } else { w_sz = sz + TXDESC_SIZE + PACKET_OFFSET_SZ; } ff_hwaddr = rtw_get_ff_hwaddr23a(pxmitframe); inner_ret = rtl8723au_write_port(padapter, ff_hwaddr, w_sz, pxmitbuf); rtw_count_tx_stats23a(padapter, pxmitframe, sz); RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, "rtw_write_port, w_sz =%d\n", w_sz); mem_addr += w_sz; mem_addr = PTR_ALIGN(mem_addr, 4); } rtw_free_xmitframe23a(pxmitpriv, pxmitframe); if (ret != _SUCCESS) rtw23a_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_UNKNOWN); return ret; } bool rtl8723au_xmitframe_complete(struct rtw_adapter *padapter, struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf) { struct hw_xmit *phwxmits; struct xmit_frame *pxmitframe; int hwentry; int res = _SUCCESS, xcnt = 0; phwxmits = pxmitpriv->hwxmits; hwentry = pxmitpriv->hwxmit_entry; RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, "xmitframe_complete()\n"); if (pxmitbuf == NULL) { pxmitbuf = rtw_alloc_xmitbuf23a(pxmitpriv); if (!pxmitbuf) return false; } pxmitframe = rtw_dequeue_xframe23a(pxmitpriv, phwxmits, hwentry); if (pxmitframe) { pxmitframe->pxmitbuf = pxmitbuf; pxmitframe->buf_addr = pxmitbuf->pbuf; pxmitbuf->priv_data = pxmitframe; if (pxmitframe->frame_tag == DATA_FRAMETAG) { if (pxmitframe->attrib.priority <= 15)/* TID0~15 */ res = rtw_xmitframe_coalesce23a(padapter, pxmitframe->pkt, pxmitframe); rtw_os_xmit_complete23a(padapter, pxmitframe);/* always return ndis_packet after rtw_xmitframe_coalesce23a */ } RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, "xmitframe_complete(): rtw_dump_xframe\n"); if (res == _SUCCESS) { rtw_dump_xframe(padapter, pxmitframe); } else { rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf); rtw_free_xmitframe23a(pxmitpriv, pxmitframe); } xcnt++; } else { rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf); return false; } return true; } static int xmitframe_direct(struct rtw_adapter *padapter, struct xmit_frame *pxmitframe) { int res; res = rtw_xmitframe_coalesce23a(padapter, pxmitframe->pkt, pxmitframe); if (res == _SUCCESS) rtw_dump_xframe(padapter, pxmitframe); return res; } /* * Return * true dump packet directly * false enqueue packet */ bool rtl8723au_hal_xmit(struct rtw_adapter *padapter, struct xmit_frame *pxmitframe) { int res; struct xmit_buf *pxmitbuf = NULL; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct pkt_attrib *pattrib = &pxmitframe->attrib; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; pattrib->qsel = pattrib->priority; spin_lock_bh(&pxmitpriv->lock); #ifdef CONFIG_8723AU_AP_MODE if (xmitframe_enqueue_for_sleeping_sta23a(padapter, pxmitframe)) { struct sta_info *psta; struct sta_priv *pstapriv = &padapter->stapriv; spin_unlock_bh(&pxmitpriv->lock); if (pattrib->psta) psta = pattrib->psta; else psta = rtw_get_stainfo23a(pstapriv, pattrib->ra); if (psta) { if (psta->sleepq_len > (NR_XMITFRAME>>3)) wakeup_sta_to_xmit23a(padapter, psta); } return false; } #endif if (rtw_txframes_sta_ac_pending23a(padapter, pattrib) > 0) goto enqueue; if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true) goto enqueue; pxmitbuf = rtw_alloc_xmitbuf23a(pxmitpriv); if (pxmitbuf == NULL) goto enqueue; spin_unlock_bh(&pxmitpriv->lock); pxmitframe->pxmitbuf = pxmitbuf; pxmitframe->buf_addr = pxmitbuf->pbuf; pxmitbuf->priv_data = pxmitframe; if (xmitframe_direct(padapter, pxmitframe) != _SUCCESS) { rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf); rtw_free_xmitframe23a(pxmitpriv, pxmitframe); } return true; enqueue: res = rtw_xmitframe_enqueue23a(padapter, pxmitframe); spin_unlock_bh(&pxmitpriv->lock); if (res != _SUCCESS) { RT_TRACE(_module_xmit_osdep_c_, _drv_err_, "pre_xmitframe: enqueue xmitframe fail\n"); rtw_free_xmitframe23a(pxmitpriv, pxmitframe); /* Trick, make the statistics correct */ pxmitpriv->tx_pkts--; pxmitpriv->tx_drop++; return true; } return false; } int rtl8723au_mgnt_xmit(struct rtw_adapter *padapter, struct xmit_frame *pmgntframe) { return rtw_dump_xframe(padapter, pmgntframe); } int rtl8723au_hal_xmitframe_enqueue(struct rtw_adapter *padapter, struct xmit_frame *pxmitframe) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; int err; err = rtw_xmitframe_enqueue23a(padapter, pxmitframe); if (err != _SUCCESS) { rtw_free_xmitframe23a(pxmitpriv, pxmitframe); /* Trick, make the statistics correct */ pxmitpriv->tx_pkts--; pxmitpriv->tx_drop++; } else { tasklet_hi_schedule(&pxmitpriv->xmit_tasklet); } return err; }