diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
commit | 57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch) | |
tree | 5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/staging/rtl8712/rtl871x_xmit.c |
Initial import
Diffstat (limited to 'drivers/staging/rtl8712/rtl871x_xmit.c')
-rw-r--r-- | drivers/staging/rtl8712/rtl871x_xmit.c | 1056 |
1 files changed, 1056 insertions, 0 deletions
diff --git a/drivers/staging/rtl8712/rtl871x_xmit.c b/drivers/staging/rtl8712/rtl871x_xmit.c new file mode 100644 index 000000000..2e4fa8895 --- /dev/null +++ b/drivers/staging/rtl8712/rtl871x_xmit.c @@ -0,0 +1,1056 @@ +/****************************************************************************** + * rtl871x_xmit.c + * + * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. + * Linux device driver for RTL8192SU + * + * 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 + * + * Modifications for inclusion into the Linux staging tree are + * Copyright(c) 2010 Larry Finger. All rights reserved. + * + * Contact information: + * WLAN FAE <wlanfae@realtek.com> + * Larry Finger <Larry.Finger@lwfinger.net> + * + ******************************************************************************/ + +#define _RTL871X_XMIT_C_ + +#include "osdep_service.h" +#include "drv_types.h" +#include "wifi.h" +#include "osdep_intf.h" +#include "usb_ops.h" + + +static const u8 P802_1H_OUI[P80211_OUI_LEN] = {0x00, 0x00, 0xf8}; +static const u8 RFC1042_OUI[P80211_OUI_LEN] = {0x00, 0x00, 0x00}; +static void init_hwxmits(struct hw_xmit *phwxmit, sint entry); +static void alloc_hwxmits(struct _adapter *padapter); +static void free_hwxmits(struct _adapter *padapter); + +static void _init_txservq(struct tx_servq *ptxservq) +{ + INIT_LIST_HEAD(&ptxservq->tx_pending); + _init_queue(&ptxservq->sta_pending); + ptxservq->qcnt = 0; +} + +void _r8712_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv) +{ + memset((unsigned char *)psta_xmitpriv, 0, + sizeof(struct sta_xmit_priv)); + spin_lock_init(&psta_xmitpriv->lock); + _init_txservq(&psta_xmitpriv->be_q); + _init_txservq(&psta_xmitpriv->bk_q); + _init_txservq(&psta_xmitpriv->vi_q); + _init_txservq(&psta_xmitpriv->vo_q); + INIT_LIST_HEAD(&psta_xmitpriv->legacy_dz); + INIT_LIST_HEAD(&psta_xmitpriv->apsd); +} + +sint _r8712_init_xmit_priv(struct xmit_priv *pxmitpriv, + struct _adapter *padapter) +{ + sint i; + struct xmit_buf *pxmitbuf; + struct xmit_frame *pxframe; + + memset((unsigned char *)pxmitpriv, 0, sizeof(struct xmit_priv)); + spin_lock_init(&pxmitpriv->lock); + /* + Please insert all the queue initialization using _init_queue below + */ + pxmitpriv->adapter = padapter; + _init_queue(&pxmitpriv->be_pending); + _init_queue(&pxmitpriv->bk_pending); + _init_queue(&pxmitpriv->vi_pending); + _init_queue(&pxmitpriv->vo_pending); + _init_queue(&pxmitpriv->bm_pending); + _init_queue(&pxmitpriv->legacy_dz_queue); + _init_queue(&pxmitpriv->apsd_queue); + _init_queue(&pxmitpriv->free_xmit_queue); + /* + Please allocate memory with the sz = (struct xmit_frame) * NR_XMITFRAME, + and initialize free_xmit_frame below. + Please also apply free_txobj to link_up all the xmit_frames... + */ + pxmitpriv->pallocated_frame_buf = kmalloc(NR_XMITFRAME * sizeof(struct xmit_frame) + 4, + GFP_ATOMIC); + if (pxmitpriv->pallocated_frame_buf == NULL) { + pxmitpriv->pxmit_frame_buf = NULL; + return _FAIL; + } + pxmitpriv->pxmit_frame_buf = pxmitpriv->pallocated_frame_buf + 4 - + ((addr_t) (pxmitpriv->pallocated_frame_buf) & 3); + pxframe = (struct xmit_frame *) pxmitpriv->pxmit_frame_buf; + for (i = 0; i < NR_XMITFRAME; i++) { + INIT_LIST_HEAD(&(pxframe->list)); + pxframe->padapter = padapter; + pxframe->frame_tag = DATA_FRAMETAG; + pxframe->pkt = NULL; + pxframe->buf_addr = NULL; + pxframe->pxmitbuf = NULL; + list_add_tail(&(pxframe->list), + &(pxmitpriv->free_xmit_queue.queue)); + pxframe++; + } + pxmitpriv->free_xmitframe_cnt = NR_XMITFRAME; + /* + init xmit hw_txqueue + */ + _r8712_init_hw_txqueue(&pxmitpriv->be_txqueue, BE_QUEUE_INX); + _r8712_init_hw_txqueue(&pxmitpriv->bk_txqueue, BK_QUEUE_INX); + _r8712_init_hw_txqueue(&pxmitpriv->vi_txqueue, VI_QUEUE_INX); + _r8712_init_hw_txqueue(&pxmitpriv->vo_txqueue, VO_QUEUE_INX); + _r8712_init_hw_txqueue(&pxmitpriv->bmc_txqueue, BMC_QUEUE_INX); + pxmitpriv->frag_len = MAX_FRAG_THRESHOLD; + pxmitpriv->txirp_cnt = 1; + /*per AC pending irp*/ + pxmitpriv->beq_cnt = 0; + pxmitpriv->bkq_cnt = 0; + pxmitpriv->viq_cnt = 0; + pxmitpriv->voq_cnt = 0; + /*init xmit_buf*/ + _init_queue(&pxmitpriv->free_xmitbuf_queue); + _init_queue(&pxmitpriv->pending_xmitbuf_queue); + pxmitpriv->pallocated_xmitbuf = kmalloc(NR_XMITBUFF * sizeof(struct xmit_buf) + 4, + GFP_ATOMIC); + if (pxmitpriv->pallocated_xmitbuf == NULL) + return _FAIL; + pxmitpriv->pxmitbuf = pxmitpriv->pallocated_xmitbuf + 4 - + ((addr_t)(pxmitpriv->pallocated_xmitbuf) & 3); + pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmitbuf; + for (i = 0; i < NR_XMITBUFF; i++) { + INIT_LIST_HEAD(&pxmitbuf->list); + pxmitbuf->pallocated_buf = kmalloc(MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ, + GFP_ATOMIC); + if (pxmitbuf->pallocated_buf == NULL) + return _FAIL; + pxmitbuf->pbuf = pxmitbuf->pallocated_buf + XMITBUF_ALIGN_SZ - + ((addr_t) (pxmitbuf->pallocated_buf) & + (XMITBUF_ALIGN_SZ - 1)); + r8712_xmit_resource_alloc(padapter, pxmitbuf); + list_add_tail(&pxmitbuf->list, + &(pxmitpriv->free_xmitbuf_queue.queue)); + pxmitbuf++; + } + pxmitpriv->free_xmitbuf_cnt = NR_XMITBUFF; + INIT_WORK(&padapter->wkFilterRxFF0, r8712_SetFilter); + alloc_hwxmits(padapter); + init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry); + tasklet_init(&pxmitpriv->xmit_tasklet, + (void(*)(unsigned long))r8712_xmit_bh, + (unsigned long)padapter); + return _SUCCESS; +} + +void _free_xmit_priv(struct xmit_priv *pxmitpriv) +{ + int i; + struct _adapter *padapter = pxmitpriv->adapter; + struct xmit_frame *pxmitframe = (struct xmit_frame *) + pxmitpriv->pxmit_frame_buf; + struct xmit_buf *pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmitbuf; + + if (pxmitpriv->pxmit_frame_buf == NULL) + return; + for (i = 0; i < NR_XMITFRAME; i++) { + r8712_xmit_complete(padapter, pxmitframe); + pxmitframe++; + } + for (i = 0; i < NR_XMITBUFF; i++) { + r8712_xmit_resource_free(padapter, pxmitbuf); + kfree(pxmitbuf->pallocated_buf); + pxmitbuf++; + } + kfree(pxmitpriv->pallocated_frame_buf); + kfree(pxmitpriv->pallocated_xmitbuf); + free_hwxmits(padapter); +} + +sint r8712_update_attrib(struct _adapter *padapter, _pkt *pkt, + struct pkt_attrib *pattrib) +{ + struct pkt_file pktfile; + struct sta_info *psta = NULL; + struct ethhdr etherhdr; + + struct tx_cmd txdesc; + + sint bmcast; + struct sta_priv *pstapriv = &padapter->stapriv; + struct security_priv *psecuritypriv = &padapter->securitypriv; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct qos_priv *pqospriv = &pmlmepriv->qospriv; + + _r8712_open_pktfile(pkt, &pktfile); + + _r8712_pktfile_read(&pktfile, (unsigned char *)ðerhdr, ETH_HLEN); + + pattrib->ether_type = ntohs(etherhdr.h_proto); + +{ + /*If driver xmit ARP packet, driver can set ps mode to initial + * setting. It stands for getting DHCP or fix IP.*/ + if (pattrib->ether_type == 0x0806) { + if (padapter->pwrctrlpriv.pwr_mode != + padapter->registrypriv.power_mgnt) { + del_timer_sync(&pmlmepriv->dhcp_timer); + r8712_set_ps_mode(padapter, padapter->registrypriv. + power_mgnt, padapter->registrypriv.smart_ps); + } + } +} + memcpy(pattrib->dst, ðerhdr.h_dest, ETH_ALEN); + memcpy(pattrib->src, ðerhdr.h_source, ETH_ALEN); + pattrib->pctrl = 0; + if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) || + (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) { + memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); + memcpy(pattrib->ta, pattrib->src, ETH_ALEN); + } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { + memcpy(pattrib->ra, get_bssid(pmlmepriv), ETH_ALEN); + memcpy(pattrib->ta, pattrib->src, ETH_ALEN); + } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { + memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); + memcpy(pattrib->ta, get_bssid(pmlmepriv), ETH_ALEN); + } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) { + /*firstly, filter packet not belongs to mp*/ + if (pattrib->ether_type != 0x8712) + return _FAIL; + /* for mp storing the txcmd per packet, + * according to the info of txcmd to update pattrib */ + /*get MP_TXDESC_SIZE bytes txcmd per packet*/ + _r8712_pktfile_read(&pktfile, (u8 *)&txdesc, TXDESC_SIZE); + memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); + memcpy(pattrib->ta, pattrib->src, ETH_ALEN); + pattrib->pctrl = 1; + } + /* r8712_xmitframe_coalesce() overwrite this!*/ + pattrib->pktlen = pktfile.pkt_len; + if (ETH_P_IP == pattrib->ether_type) { + /* The following is for DHCP and ARP packet, we use cck1M to + * tx these packets and let LPS awake some time + * to prevent DHCP protocol fail */ + u8 tmp[24]; + + _r8712_pktfile_read(&pktfile, &tmp[0], 24); + pattrib->dhcp_pkt = 0; + if (pktfile.pkt_len > 282) {/*MINIMUM_DHCP_PACKET_SIZE)*/ + if (ETH_P_IP == pattrib->ether_type) {/* IP header*/ + if (((tmp[21] == 68) && (tmp[23] == 67)) || + ((tmp[21] == 67) && (tmp[23] == 68))) { + /* 68 : UDP BOOTP client + * 67 : UDP BOOTP server + * Use low rate to send DHCP packet.*/ + pattrib->dhcp_pkt = 1; + } + } + } + } + bmcast = IS_MCAST(pattrib->ra); + /* get sta_info*/ + if (bmcast) { + psta = r8712_get_bcmc_stainfo(padapter); + pattrib->mac_id = 4; + } else { + if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) { + psta = r8712_get_stainfo(pstapriv, + get_bssid(pmlmepriv)); + pattrib->mac_id = 5; + } else { + psta = r8712_get_stainfo(pstapriv, pattrib->ra); + if (psta == NULL) /* drop the pkt */ + return _FAIL; + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) + pattrib->mac_id = 5; + else + pattrib->mac_id = psta->mac_id; + } + } + + if (psta) { + pattrib->psta = psta; + } else { + /* if we cannot get psta => drrp the pkt */ + return _FAIL; + } + + pattrib->ack_policy = 0; + /* get ether_hdr_len */ + pattrib->pkt_hdrlen = ETH_HLEN; + + if (pqospriv->qos_option) + r8712_set_qos(&pktfile, pattrib); + else { + pattrib->hdrlen = WLAN_HDR_A3_LEN; + pattrib->subtype = WIFI_DATA_TYPE; + pattrib->priority = 0; + } + if (psta->ieee8021x_blocked == true) { + pattrib->encrypt = 0; + if ((pattrib->ether_type != 0x888e) && + (check_fwstate(pmlmepriv, WIFI_MP_STATE) == false)) + return _FAIL; + } else + GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt, bmcast); + switch (pattrib->encrypt) { + case _WEP40_: + case _WEP104_: + pattrib->iv_len = 4; + pattrib->icv_len = 4; + break; + case _TKIP_: + pattrib->iv_len = 8; + pattrib->icv_len = 4; + if (padapter->securitypriv.busetkipkey == _FAIL) + return _FAIL; + break; + case _AES_: + pattrib->iv_len = 8; + pattrib->icv_len = 8; + break; + default: + pattrib->iv_len = 0; + pattrib->icv_len = 0; + break; + } + + if (pattrib->encrypt && + ((padapter->securitypriv.sw_encrypt == true) || + (psecuritypriv->hw_decrypted == false))) + pattrib->bswenc = true; + else + pattrib->bswenc = false; + /* if in MP_STATE, update pkt_attrib from mp_txcmd, and overwrite + * some settings above.*/ + if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) + pattrib->priority = (txdesc.txdw1 >> QSEL_SHT) & 0x1f; + return _SUCCESS; +} + +static sint xmitframe_addmic(struct _adapter *padapter, + struct xmit_frame *pxmitframe) +{ + u32 curfragnum, length; + u8 *pframe, *payload, mic[8]; + struct mic_data micdata; + struct sta_info *stainfo; + struct qos_priv *pqospriv = &(padapter->mlmepriv.qospriv); + struct pkt_attrib *pattrib = &pxmitframe->attrib; + struct security_priv *psecuritypriv = &padapter->securitypriv; + struct xmit_priv *pxmitpriv = &padapter->xmitpriv; + u8 priority[4] = {0x0, 0x0, 0x0, 0x0}; + sint bmcst = IS_MCAST(pattrib->ra); + + if (pattrib->psta) + stainfo = pattrib->psta; + else + stainfo = r8712_get_stainfo(&padapter->stapriv, + &pattrib->ra[0]); + if (pattrib->encrypt == _TKIP_) { + /*encode mic code*/ + if (stainfo != NULL) { + u8 null_key[16] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0}; + pframe = pxmitframe->buf_addr + TXDESC_OFFSET; + if (bmcst) { + if (!memcmp(psecuritypriv->XGrptxmickey + [psecuritypriv->XGrpKeyid].skey, + null_key, 16)) + return _FAIL; + /*start to calculate the mic code*/ + r8712_secmicsetkey(&micdata, + psecuritypriv-> + XGrptxmickey[psecuritypriv-> + XGrpKeyid].skey); + } else { + if (!memcmp(&stainfo->tkiptxmickey.skey[0], + null_key, 16)) + return _FAIL; + /* start to calculate the mic code */ + r8712_secmicsetkey(&micdata, + &stainfo->tkiptxmickey.skey[0]); + } + if (pframe[1] & 1) { /* ToDS==1 */ + r8712_secmicappend(&micdata, + &pframe[16], 6); /*DA*/ + if (pframe[1]&2) /* From Ds==1 */ + r8712_secmicappend(&micdata, + &pframe[24], 6); + else + r8712_secmicappend(&micdata, + &pframe[10], 6); + } else { /* ToDS==0 */ + r8712_secmicappend(&micdata, + &pframe[4], 6); /* DA */ + if (pframe[1]&2) /* From Ds==1 */ + r8712_secmicappend(&micdata, + &pframe[16], 6); + else + r8712_secmicappend(&micdata, + &pframe[10], 6); + } + if (pqospriv->qos_option == 1) + priority[0] = (u8)pxmitframe-> + attrib.priority; + r8712_secmicappend(&micdata, &priority[0], 4); + payload = pframe; + for (curfragnum = 0; curfragnum < pattrib->nr_frags; + curfragnum++) { + payload = (u8 *)RND4((addr_t)(payload)); + payload = payload+pattrib-> + hdrlen+pattrib->iv_len; + if ((curfragnum + 1) == pattrib->nr_frags) { + length = pattrib->last_txcmdsz - + pattrib->hdrlen - + pattrib->iv_len - + ((psecuritypriv->sw_encrypt) + ? pattrib->icv_len : 0); + r8712_secmicappend(&micdata, payload, + length); + payload = payload+length; + } else{ + length = pxmitpriv->frag_len - + pattrib->hdrlen-pattrib->iv_len - + ((psecuritypriv->sw_encrypt) ? + pattrib->icv_len : 0); + r8712_secmicappend(&micdata, payload, + length); + payload = payload + length + + pattrib->icv_len; + } + } + r8712_secgetmic(&micdata, &(mic[0])); + /* add mic code and add the mic code length in + * last_txcmdsz */ + memcpy(payload, &(mic[0]), 8); + pattrib->last_txcmdsz += 8; + payload = payload-pattrib->last_txcmdsz + 8; + } + } + return _SUCCESS; +} + +static sint xmitframe_swencrypt(struct _adapter *padapter, + struct xmit_frame *pxmitframe) +{ + struct pkt_attrib *pattrib = &pxmitframe->attrib; + + if (pattrib->bswenc) { + switch (pattrib->encrypt) { + case _WEP40_: + case _WEP104_: + r8712_wep_encrypt(padapter, (u8 *)pxmitframe); + break; + case _TKIP_: + r8712_tkip_encrypt(padapter, (u8 *)pxmitframe); + break; + case _AES_: + r8712_aes_encrypt(padapter, (u8 *)pxmitframe); + break; + default: + break; + } + } + return _SUCCESS; +} + +static sint make_wlanhdr(struct _adapter *padapter, u8 *hdr, + struct pkt_attrib *pattrib) +{ + u16 *qc; + + struct ieee80211_hdr *pwlanhdr = (struct ieee80211_hdr *)hdr; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct qos_priv *pqospriv = &pmlmepriv->qospriv; + u16 *fctrl = &pwlanhdr->frame_ctl; + + memset(hdr, 0, WLANHDR_OFFSET); + SetFrameSubType(fctrl, pattrib->subtype); + if (pattrib->subtype & WIFI_DATA_TYPE) { + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) { + /* to_ds = 1, fr_ds = 0; */ + SetToDs(fctrl); + memcpy(pwlanhdr->addr1, get_bssid(pmlmepriv), + ETH_ALEN); + memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN); + memcpy(pwlanhdr->addr3, pattrib->dst, ETH_ALEN); + } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { + /* to_ds = 0, fr_ds = 1; */ + SetFrDs(fctrl); + memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN); + memcpy(pwlanhdr->addr2, get_bssid(pmlmepriv), + ETH_ALEN); + memcpy(pwlanhdr->addr3, pattrib->src, ETH_ALEN); + } else if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) + || (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) + == true)) { + memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN); + memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN); + memcpy(pwlanhdr->addr3, get_bssid(pmlmepriv), + ETH_ALEN); + } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) { + memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN); + memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN); + memcpy(pwlanhdr->addr3, get_bssid(pmlmepriv), + ETH_ALEN); + } else + return _FAIL; + + if (pattrib->encrypt) + SetPrivacy(fctrl); + if (pqospriv->qos_option) { + qc = (unsigned short *)(hdr + pattrib->hdrlen - 2); + if (pattrib->priority) + SetPriority(qc, pattrib->priority); + SetAckpolicy(qc, pattrib->ack_policy); + } + /* TODO: fill HT Control Field */ + /* Update Seq Num will be handled by f/w */ + { + struct sta_info *psta; + sint bmcst = IS_MCAST(pattrib->ra); + + if (pattrib->psta) + psta = pattrib->psta; + else { + if (bmcst) + psta = r8712_get_bcmc_stainfo(padapter); + else + psta = + r8712_get_stainfo(&padapter->stapriv, + pattrib->ra); + } + if (psta) { + psta->sta_xmitpriv.txseq_tid + [pattrib->priority]++; + psta->sta_xmitpriv.txseq_tid[pattrib->priority] + &= 0xFFF; + pattrib->seqnum = psta->sta_xmitpriv. + txseq_tid[pattrib->priority]; + SetSeqNum(hdr, pattrib->seqnum); + } + } + } + return _SUCCESS; +} + +static sint r8712_put_snap(u8 *data, u16 h_proto) +{ + struct ieee80211_snap_hdr *snap; + const u8 *oui; + + snap = (struct ieee80211_snap_hdr *)data; + snap->dsap = 0xaa; + snap->ssap = 0xaa; + snap->ctrl = 0x03; + if (h_proto == 0x8137 || h_proto == 0x80f3) + oui = P802_1H_OUI; + else + oui = RFC1042_OUI; + snap->oui[0] = oui[0]; + snap->oui[1] = oui[1]; + snap->oui[2] = oui[2]; + *(u16 *)(data + SNAP_SIZE) = htons(h_proto); + return SNAP_SIZE + sizeof(u16); +} + +/* + * This sub-routine will perform all the following: + * 1. remove 802.3 header. + * 2. create wlan_header, based on the info in pxmitframe + * 3. append sta's iv/ext-iv + * 4. append LLC + * 5. move frag chunk from pframe to pxmitframe->mem + * 6. apply sw-encrypt, if necessary. + */ +sint r8712_xmitframe_coalesce(struct _adapter *padapter, _pkt *pkt, + struct xmit_frame *pxmitframe) +{ + struct pkt_file pktfile; + + sint frg_len, mpdu_len, llc_sz; + u32 mem_sz; + u8 frg_inx; + addr_t addr; + u8 *pframe, *mem_start, *ptxdesc; + struct sta_info *psta; + struct security_priv *psecuritypriv = &padapter->securitypriv; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + struct xmit_priv *pxmitpriv = &padapter->xmitpriv; + struct pkt_attrib *pattrib = &pxmitframe->attrib; + u8 *pbuf_start; + sint bmcst = IS_MCAST(pattrib->ra); + + if (pattrib->psta == NULL) + return _FAIL; + psta = pattrib->psta; + if (pxmitframe->buf_addr == NULL) + return _FAIL; + pbuf_start = pxmitframe->buf_addr; + ptxdesc = pbuf_start; + mem_start = pbuf_start + TXDESC_OFFSET; + if (make_wlanhdr(padapter, mem_start, pattrib) == _FAIL) + return _FAIL; + _r8712_open_pktfile(pkt, &pktfile); + _r8712_pktfile_read(&pktfile, NULL, (uint) pattrib->pkt_hdrlen); + if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) { + /* truncate TXDESC_SIZE bytes txcmd if at mp mode for 871x */ + if (pattrib->ether_type == 0x8712) { + /* take care - update_txdesc overwrite this */ + _r8712_pktfile_read(&pktfile, ptxdesc, TXDESC_SIZE); + } + } + pattrib->pktlen = pktfile.pkt_len; + frg_inx = 0; + frg_len = pxmitpriv->frag_len - 4; + while (1) { + llc_sz = 0; + mpdu_len = frg_len; + pframe = mem_start; + SetMFrag(mem_start); + pframe += pattrib->hdrlen; + mpdu_len -= pattrib->hdrlen; + /* adding icv, if necessary...*/ + if (pattrib->iv_len) { + if (psta != NULL) { + switch (pattrib->encrypt) { + case _WEP40_: + case _WEP104_: + WEP_IV(pattrib->iv, psta->txpn, + (u8)psecuritypriv-> + PrivacyKeyIndex); + break; + case _TKIP_: + if (bmcst) + TKIP_IV(pattrib->iv, + psta->txpn, + (u8)psecuritypriv-> + XGrpKeyid); + else + TKIP_IV(pattrib->iv, psta->txpn, + 0); + break; + case _AES_: + if (bmcst) + AES_IV(pattrib->iv, psta->txpn, + (u8)psecuritypriv-> + XGrpKeyid); + else + AES_IV(pattrib->iv, psta->txpn, + 0); + break; + } + } + memcpy(pframe, pattrib->iv, pattrib->iv_len); + pframe += pattrib->iv_len; + mpdu_len -= pattrib->iv_len; + } + if (frg_inx == 0) { + llc_sz = r8712_put_snap(pframe, pattrib->ether_type); + pframe += llc_sz; + mpdu_len -= llc_sz; + } + if ((pattrib->icv_len > 0) && (pattrib->bswenc)) + mpdu_len -= pattrib->icv_len; + if (bmcst) + mem_sz = _r8712_pktfile_read(&pktfile, pframe, + pattrib->pktlen); + else + mem_sz = _r8712_pktfile_read(&pktfile, pframe, + mpdu_len); + pframe += mem_sz; + if ((pattrib->icv_len > 0) && (pattrib->bswenc)) { + memcpy(pframe, pattrib->icv, pattrib->icv_len); + pframe += pattrib->icv_len; + } + frg_inx++; + if (bmcst || (r8712_endofpktfile(&pktfile) == true)) { + pattrib->nr_frags = frg_inx; + pattrib->last_txcmdsz = pattrib->hdrlen + + pattrib->iv_len + + ((pattrib->nr_frags == 1) ? + llc_sz : 0) + + ((pattrib->bswenc) ? + pattrib->icv_len : 0) + mem_sz; + ClearMFrag(mem_start); + break; + } + addr = (addr_t)(pframe); + mem_start = (unsigned char *)RND4(addr) + TXDESC_OFFSET; + memcpy(mem_start, pbuf_start + TXDESC_OFFSET, pattrib->hdrlen); + } + + if (xmitframe_addmic(padapter, pxmitframe) == _FAIL) + return _FAIL; + xmitframe_swencrypt(padapter, pxmitframe); + return _SUCCESS; +} + +void r8712_update_protection(struct _adapter *padapter, u8 *ie, uint ie_len) +{ + uint protection; + u8 *perp; + sint erp_len; + struct xmit_priv *pxmitpriv = &padapter->xmitpriv; + struct registry_priv *pregistrypriv = &padapter->registrypriv; + + switch (pxmitpriv->vcs_setting) { + case DISABLE_VCS: + pxmitpriv->vcs = NONE_VCS; + break; + case ENABLE_VCS: + break; + case AUTO_VCS: + default: + perp = r8712_get_ie(ie, _ERPINFO_IE_, &erp_len, ie_len); + if (perp == NULL) + pxmitpriv->vcs = NONE_VCS; + else { + protection = (*(perp + 2)) & BIT(1); + if (protection) { + if (pregistrypriv->vcs_type == RTS_CTS) + pxmitpriv->vcs = RTS_CTS; + else + pxmitpriv->vcs = CTS_TO_SELF; + } else + pxmitpriv->vcs = NONE_VCS; + } + break; + } +} + +struct xmit_buf *r8712_alloc_xmitbuf(struct xmit_priv *pxmitpriv) +{ + unsigned long irqL; + struct xmit_buf *pxmitbuf = NULL; + struct list_head *plist, *phead; + struct __queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue; + + spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irqL); + if (list_empty(&pfree_xmitbuf_queue->queue)) + pxmitbuf = NULL; + else { + phead = &pfree_xmitbuf_queue->queue; + plist = phead->next; + pxmitbuf = LIST_CONTAINOR(plist, struct xmit_buf, list); + list_del_init(&(pxmitbuf->list)); + } + if (pxmitbuf != NULL) + pxmitpriv->free_xmitbuf_cnt--; + spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irqL); + return pxmitbuf; +} + +int r8712_free_xmitbuf(struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf) +{ + unsigned long irqL; + struct __queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue; + + if (pxmitbuf == NULL) + return _FAIL; + spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irqL); + list_del_init(&pxmitbuf->list); + list_add_tail(&(pxmitbuf->list), &pfree_xmitbuf_queue->queue); + pxmitpriv->free_xmitbuf_cnt++; + spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irqL); + return _SUCCESS; +} + +/* +Calling context: +1. OS_TXENTRY +2. RXENTRY (rx_thread or RX_ISR/RX_CallBack) + +If we turn on USE_RXTHREAD, then, no need for critical section. +Otherwise, we must use _enter/_exit critical to protect free_xmit_queue... + +Must be very very cautious... + +*/ + +struct xmit_frame *r8712_alloc_xmitframe(struct xmit_priv *pxmitpriv) +{ + /* + Please remember to use all the osdep_service api, + and lock/unlock or _enter/_exit critical to protect + pfree_xmit_queue + */ + unsigned long irqL; + struct xmit_frame *pxframe = NULL; + struct list_head *plist, *phead; + struct __queue *pfree_xmit_queue = &pxmitpriv->free_xmit_queue; + + spin_lock_irqsave(&pfree_xmit_queue->lock, irqL); + if (list_empty(&pfree_xmit_queue->queue)) + pxframe = NULL; + else { + phead = &pfree_xmit_queue->queue; + plist = phead->next; + pxframe = LIST_CONTAINOR(plist, struct xmit_frame, list); + list_del_init(&(pxframe->list)); + } + if (pxframe != NULL) { + pxmitpriv->free_xmitframe_cnt--; + pxframe->buf_addr = NULL; + pxframe->pxmitbuf = NULL; + pxframe->attrib.psta = NULL; + pxframe->pkt = NULL; + } + spin_unlock_irqrestore(&pfree_xmit_queue->lock, irqL); + return pxframe; +} + +void r8712_free_xmitframe(struct xmit_priv *pxmitpriv, + struct xmit_frame *pxmitframe) +{ + unsigned long irqL; + struct __queue *pfree_xmit_queue = &pxmitpriv->free_xmit_queue; + struct _adapter *padapter = pxmitpriv->adapter; + + if (pxmitframe == NULL) + return; + spin_lock_irqsave(&pfree_xmit_queue->lock, irqL); + list_del_init(&pxmitframe->list); + if (pxmitframe->pkt) + pxmitframe->pkt = NULL; + list_add_tail(&pxmitframe->list, &pfree_xmit_queue->queue); + pxmitpriv->free_xmitframe_cnt++; + spin_unlock_irqrestore(&pfree_xmit_queue->lock, irqL); + if (netif_queue_stopped(padapter->pnetdev)) + netif_wake_queue(padapter->pnetdev); +} + +void r8712_free_xmitframe_ex(struct xmit_priv *pxmitpriv, + struct xmit_frame *pxmitframe) +{ + if (pxmitframe == NULL) + return; + if (pxmitframe->frame_tag == DATA_FRAMETAG) + r8712_free_xmitframe(pxmitpriv, pxmitframe); +} + +void r8712_free_xmitframe_queue(struct xmit_priv *pxmitpriv, + struct __queue *pframequeue) +{ + unsigned long irqL; + struct list_head *plist, *phead; + struct xmit_frame *pxmitframe; + + spin_lock_irqsave(&(pframequeue->lock), irqL); + phead = &pframequeue->queue; + plist = phead->next; + while (end_of_queue_search(phead, plist) == false) { + pxmitframe = LIST_CONTAINOR(plist, struct xmit_frame, list); + plist = plist->next; + r8712_free_xmitframe(pxmitpriv, pxmitframe); + } + spin_unlock_irqrestore(&(pframequeue->lock), irqL); +} + +static inline struct tx_servq *get_sta_pending(struct _adapter *padapter, + struct __queue **ppstapending, + struct sta_info *psta, sint up) +{ + + struct tx_servq *ptxservq; + struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits; + + switch (up) { + case 1: + case 2: + ptxservq = &(psta->sta_xmitpriv.bk_q); + *ppstapending = &padapter->xmitpriv.bk_pending; + (phwxmits+3)->accnt++; + break; + case 4: + case 5: + ptxservq = &(psta->sta_xmitpriv.vi_q); + *ppstapending = &padapter->xmitpriv.vi_pending; + (phwxmits+1)->accnt++; + break; + case 6: + case 7: + ptxservq = &(psta->sta_xmitpriv.vo_q); + *ppstapending = &padapter->xmitpriv.vo_pending; + (phwxmits+0)->accnt++; + break; + case 0: + case 3: + default: + ptxservq = &(psta->sta_xmitpriv.be_q); + *ppstapending = &padapter->xmitpriv.be_pending; + (phwxmits + 2)->accnt++; + break; + } + return ptxservq; +} + +/* + * Will enqueue pxmitframe to the proper queue, and indicate it + * to xx_pending list..... + */ +sint r8712_xmit_classifier(struct _adapter *padapter, + struct xmit_frame *pxmitframe) +{ + unsigned long irqL0; + struct __queue *pstapending; + struct sta_info *psta; + struct tx_servq *ptxservq; + struct pkt_attrib *pattrib = &pxmitframe->attrib; + struct sta_priv *pstapriv = &padapter->stapriv; + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; + sint bmcst = IS_MCAST(pattrib->ra); + + if (pattrib->psta) + psta = pattrib->psta; + else { + if (bmcst) + psta = r8712_get_bcmc_stainfo(padapter); + else { + if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) + psta = r8712_get_stainfo(pstapriv, + get_bssid(pmlmepriv)); + else + psta = r8712_get_stainfo(pstapriv, pattrib->ra); + } + } + if (psta == NULL) + return _FAIL; + ptxservq = get_sta_pending(padapter, &pstapending, + psta, pattrib->priority); + spin_lock_irqsave(&pstapending->lock, irqL0); + if (list_empty(&ptxservq->tx_pending)) + list_add_tail(&ptxservq->tx_pending, &pstapending->queue); + list_add_tail(&pxmitframe->list, &ptxservq->sta_pending.queue); + ptxservq->qcnt++; + spin_unlock_irqrestore(&pstapending->lock, irqL0); + return _SUCCESS; +} + +static void alloc_hwxmits(struct _adapter *padapter) +{ + struct hw_xmit *hwxmits; + struct xmit_priv *pxmitpriv = &padapter->xmitpriv; + + pxmitpriv->hwxmit_entry = HWXMIT_ENTRY; + pxmitpriv->hwxmits = kmalloc_array(pxmitpriv->hwxmit_entry, + sizeof(struct hw_xmit), GFP_ATOMIC); + if (pxmitpriv->hwxmits == NULL) + return; + hwxmits = pxmitpriv->hwxmits; + if (pxmitpriv->hwxmit_entry == 5) { + pxmitpriv->bmc_txqueue.head = 0; + hwxmits[0] .phwtxqueue = &pxmitpriv->bmc_txqueue; + hwxmits[0] .sta_queue = &pxmitpriv->bm_pending; + pxmitpriv->vo_txqueue.head = 0; + hwxmits[1] .phwtxqueue = &pxmitpriv->vo_txqueue; + hwxmits[1] .sta_queue = &pxmitpriv->vo_pending; + pxmitpriv->vi_txqueue.head = 0; + hwxmits[2] .phwtxqueue = &pxmitpriv->vi_txqueue; + hwxmits[2] .sta_queue = &pxmitpriv->vi_pending; + pxmitpriv->bk_txqueue.head = 0; + hwxmits[3] .phwtxqueue = &pxmitpriv->bk_txqueue; + hwxmits[3] .sta_queue = &pxmitpriv->bk_pending; + pxmitpriv->be_txqueue.head = 0; + hwxmits[4] .phwtxqueue = &pxmitpriv->be_txqueue; + hwxmits[4] .sta_queue = &pxmitpriv->be_pending; + } else if (pxmitpriv->hwxmit_entry == 4) { + pxmitpriv->vo_txqueue.head = 0; + hwxmits[0] .phwtxqueue = &pxmitpriv->vo_txqueue; + hwxmits[0] .sta_queue = &pxmitpriv->vo_pending; + pxmitpriv->vi_txqueue.head = 0; + hwxmits[1] .phwtxqueue = &pxmitpriv->vi_txqueue; + hwxmits[1] .sta_queue = &pxmitpriv->vi_pending; + pxmitpriv->be_txqueue.head = 0; + hwxmits[2] .phwtxqueue = &pxmitpriv->be_txqueue; + hwxmits[2] .sta_queue = &pxmitpriv->be_pending; + pxmitpriv->bk_txqueue.head = 0; + hwxmits[3] .phwtxqueue = &pxmitpriv->bk_txqueue; + hwxmits[3] .sta_queue = &pxmitpriv->bk_pending; + } +} + +static void free_hwxmits(struct _adapter *padapter) +{ + struct xmit_priv *pxmitpriv = &padapter->xmitpriv; + + kfree(pxmitpriv->hwxmits); +} + +static void init_hwxmits(struct hw_xmit *phwxmit, sint entry) +{ + sint i; + + for (i = 0; i < entry; i++, phwxmit++) { + spin_lock_init(&phwxmit->xmit_lock); + INIT_LIST_HEAD(&phwxmit->pending); + phwxmit->txcmdcnt = 0; + phwxmit->accnt = 0; + } +} + +void xmitframe_xmitbuf_attach(struct xmit_frame *pxmitframe, + struct xmit_buf *pxmitbuf) +{ + /* pxmitbuf attach to pxmitframe */ + pxmitframe->pxmitbuf = pxmitbuf; + /* urb and irp connection */ + pxmitframe->pxmit_urb[0] = pxmitbuf->pxmit_urb[0]; + /* buffer addr assoc */ + pxmitframe->buf_addr = pxmitbuf->pbuf; + /* pxmitframe attach to pxmitbuf */ + pxmitbuf->priv_data = pxmitframe; +} + +/* + * tx_action == 0 == no frames to transmit + * tx_action > 0 ==> we have frames to transmit + * tx_action < 0 ==> we have frames to transmit, but TXFF is not even enough + * to transmit 1 frame. + */ + +int r8712_pre_xmit(struct _adapter *padapter, struct xmit_frame *pxmitframe) +{ + unsigned long irqL; + int ret; + struct xmit_buf *pxmitbuf = NULL; + struct xmit_priv *pxmitpriv = &padapter->xmitpriv; + struct pkt_attrib *pattrib = &pxmitframe->attrib; + + r8712_do_queue_select(padapter, pattrib); + spin_lock_irqsave(&pxmitpriv->lock, irqL); + if (r8712_txframes_sta_ac_pending(padapter, pattrib) > 0) { + ret = false; + r8712_xmit_enqueue(padapter, pxmitframe); + spin_unlock_irqrestore(&pxmitpriv->lock, irqL); + return ret; + } + pxmitbuf = r8712_alloc_xmitbuf(pxmitpriv); + if (pxmitbuf == NULL) { /*enqueue packet*/ + ret = false; + r8712_xmit_enqueue(padapter, pxmitframe); + spin_unlock_irqrestore(&pxmitpriv->lock, irqL); + } else { /*dump packet directly*/ + spin_unlock_irqrestore(&pxmitpriv->lock, irqL); + ret = true; + xmitframe_xmitbuf_attach(pxmitframe, pxmitbuf); + r8712_xmit_direct(padapter, pxmitframe); + } + return ret; +} |