/****************************************************************************** * * Copyright(c) 2007 - 2019 Realtek Corporation. * * 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. * *****************************************************************************/ #include #ifdef CONFIG_CORE_TXSC u8 DBG_PRINT_MDATA_ONCE; void _print_txreq_mdata(struct rtw_t_meta_data *mdata, const char *func) { if (DBG_PRINT_MDATA_ONCE == 1) { RTW_PRINT("[%s]\n", func); RTW_PRINT("da: %02x%02x%02x%02x%02x%02x\n", mdata->da[0], mdata->da[1], mdata->da[2], mdata->da[3], mdata->da[4], mdata->da[5]); RTW_PRINT("sa: %02x%02x%02x%02x%02x%02x\n", mdata->sa[0], mdata->sa[1], mdata->sa[2], mdata->sa[3], mdata->sa[4], mdata->sa[5]); RTW_PRINT("to_ds: %d\n", mdata->to_ds); RTW_PRINT("from_ds: %d\n", mdata->from_ds); RTW_PRINT("band: %d\n", mdata->band); RTW_PRINT("type: %d\n", mdata->type); RTW_PRINT("hw_seq_mode: %d\n", mdata->hw_seq_mode); RTW_PRINT("hw_ssn_sel: %d\n", mdata->hw_ssn_sel); RTW_PRINT("smh_en: %d\n", mdata->smh_en); RTW_PRINT("hw_amsdu: %d\n", mdata->hw_amsdu); RTW_PRINT("hw_sec_iv: %d\n", mdata->hw_sec_iv); RTW_PRINT("wd_page_size: %d\n", mdata->wd_page_size); RTW_PRINT("hdr_len: %d\n", mdata->hdr_len); RTW_PRINT("dma_ch: %d\n", mdata->dma_ch); RTW_PRINT("usb_pkt_ofst: %d\n", mdata->usb_pkt_ofst); RTW_PRINT("wdinfo_en: %d\n", mdata->wdinfo_en); RTW_PRINT("wp_offset: %d\n", mdata->wp_offset); RTW_PRINT("shcut_camid: %d\n", mdata->shcut_camid); RTW_PRINT("usb_txagg_num: %d\n", mdata->usb_txagg_num); RTW_PRINT("pktlen: %d\n", mdata->pktlen); RTW_PRINT("tid: %d\n", mdata->tid); RTW_PRINT("macid: %d\n", mdata->macid); RTW_PRINT("sw_seq: %d\n", mdata->sw_seq); RTW_PRINT("ampdu_en: %d\n", mdata->ampdu_en); RTW_PRINT("bk: %d\n", mdata->bk); RTW_PRINT("mbssid: %d\n", mdata->mbssid); RTW_PRINT("hal_port: %d\n", mdata->hal_port); RTW_PRINT("data_bw_er: %d\n", mdata->data_bw_er); RTW_PRINT("dis_rts_rate_fb: %d\n", mdata->dis_rts_rate_fb); RTW_PRINT("dis_data_rate_fb: %d\n", mdata->dis_data_rate_fb); RTW_PRINT("f_ldpc: %d\n", mdata->f_ldpc); RTW_PRINT("f_stbc: %d\n", mdata->f_stbc); RTW_PRINT("f_dcm: %d\n", mdata->f_dcm); RTW_PRINT("f_er: %d\n", mdata->f_er); RTW_PRINT("f_rate: %d\n", mdata->f_rate); RTW_PRINT("f_gi_ltf: %d\n", mdata->f_gi_ltf); RTW_PRINT("f_bw: %d\n", mdata->f_bw); RTW_PRINT("userate_sel: %d\n", mdata->userate_sel); RTW_PRINT("ack_ch_info: %d\n", mdata->ack_ch_info); RTW_PRINT("max_agg_num: %d\n", mdata->max_agg_num); RTW_PRINT("nav_use_hdr: %d\n", mdata->nav_use_hdr); RTW_PRINT("bc: %d\n", mdata->bc); RTW_PRINT("mc: %d\n", mdata->mc); RTW_PRINT("a_ctrl_bqr: %d\n", mdata->a_ctrl_bqr); RTW_PRINT("a_ctrl_uph: %d\n", mdata->a_ctrl_uph); RTW_PRINT("a_ctrl_bsr: %d\n", mdata->a_ctrl_bsr); RTW_PRINT("a_ctrl_cas: %d\n", mdata->a_ctrl_cas); RTW_PRINT("data_rty_lowest_rate: %d\n", mdata->data_rty_lowest_rate); RTW_PRINT("data_tx_cnt_lmt: %d\n", mdata->data_tx_cnt_lmt); RTW_PRINT("data_tx_cnt_lmt_en: %d\n", mdata->data_tx_cnt_lmt_en); RTW_PRINT("sec_cam_idx: %d\n", mdata->sec_cam_idx); RTW_PRINT("sec_hw_enc: %d\n", mdata->sec_hw_enc); RTW_PRINT("sec_type: %d\n", mdata->sec_type); RTW_PRINT("life_time_sel: %d\n", mdata->life_time_sel); RTW_PRINT("ampdu_density: %d\n", mdata->ampdu_density); RTW_PRINT("no_ack: %d\n", mdata->no_ack); RTW_PRINT("ndpa: %d\n", mdata->ndpa); RTW_PRINT("snd_pkt_sel: %d\n", mdata->snd_pkt_sel); RTW_PRINT("sifs_tx: %d\n", mdata->sifs_tx); RTW_PRINT("rtt_en: %d\n", mdata->rtt_en); RTW_PRINT("spe_rpt: %d\n", mdata->spe_rpt); RTW_PRINT("raw: %d\n", mdata->raw); RTW_PRINT("sw_define: %d\n", mdata->sw_define); RTW_PRINT("rts_en: %d\n", mdata->rts_en); RTW_PRINT("cts2self: %d\n", mdata->cts2self); RTW_PRINT("rts_cca_mode: %d\n", mdata->rts_cca_mode); RTW_PRINT("hw_rts_en: %d\n", mdata->hw_rts_en); DBG_PRINT_MDATA_ONCE = 0; } } u8 DBG_PRINT_TXREQ_ONCE; void _print_txreq_pklist(struct xmit_frame *pxframe, struct rtw_xmit_req *ptxreq, struct sk_buff *pskb, const char *func) { struct rtw_pkt_buf_list *pkt_list = NULL; struct rtw_xmit_req *txreq = NULL; u8 pkt_cnt = 0, i; if (DBG_PRINT_TXREQ_ONCE == 1) { RTW_PRINT("%s\n", func); RTW_PRINT("[%s] pxframe=%p txreq=%p\n", func, pxframe, ptxreq); if (pskb) txsc_dump_data(pskb->data, ETH_HLEN, "ETHHDR"); if (ptxreq != NULL) txreq = ptxreq; else txreq = pxframe->phl_txreq; pkt_list = (struct rtw_pkt_buf_list *)txreq->pkt_list; pkt_cnt = txreq->pkt_cnt; RTW_PRINT("os_priv:%p, treq_type:%d, pkt_cnt:%d, total_len:%d, shortcut_id:%d\n\n", txreq->os_priv, txreq->treq_type, txreq->pkt_cnt, txreq->total_len, txreq->shortcut_id); for (i = 0; i < pkt_cnt; i++) { RTW_PRINT("pkt_list[%d]\n", i); txsc_dump_data(pkt_list->vir_addr, pkt_list->length, "pkt_list"); pkt_list++; } DBG_PRINT_TXREQ_ONCE = 0; } } void txsc_init(_adapter *padapter) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; pxmitpriv->txsc_enable = 1; /* default TXSC on */ pxmitpriv->txsc_debug_mode = 0; pxmitpriv->txsc_debug_mask = 0x3; } void txsc_clear(_adapter *padapter) { struct sta_priv *pstapriv = &padapter->stapriv; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct sta_info *psta = NULL; int i; u8 tmp = 0; RTW_PRINT("[TXSC] clear txsc entry\n"); _rtw_spinlock_bh(&pxmitpriv->txsc_lock); if (pxmitpriv->txsc_enable) { tmp = pxmitpriv->txsc_enable; pxmitpriv->txsc_enable = 0; } pxmitpriv->ptxsc_sta_cached = NULL; for (i = 0; i < pstapriv->max_num_sta; i++) { psta = pstapriv->sta_aid[i]; if (!psta) continue; psta->txsc_cache_num = 0; psta->txsc_cur_idx = 0; psta->txsc_cache_idx = 0; psta->txsc_cache_hit = 0; psta->txsc_cache_miss = 0; psta->txsc_path_slow = 0; _rtw_memset(psta->txsc_entry_cache, 0x0, sizeof(struct txsc_entry) * CORE_TXSC_ENTRY_NUM); } pxmitpriv->txsc_enable = tmp; _rtw_spinunlock_bh(&pxmitpriv->txsc_lock); } void txsc_dump(_adapter *padapter) { struct sta_priv *pstapriv = &padapter->stapriv; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct sta_info *psta = NULL; int i, j; RTW_PRINT("[txsc][core] (txsc,enable) txsc_enable:%x\n", pxmitpriv->txsc_enable); RTW_PRINT("[txsc][core] (txsc,debug) txsc_debug_mode:%x\n", pxmitpriv->txsc_debug_mode); RTW_PRINT("[txsc][core] txsc_phl_err_cnt1:%d\n", pxmitpriv->txsc_phl_err_cnt1); RTW_PRINT("[txsc][core] txsc_phl_err_cnt2:%d\n", pxmitpriv->txsc_phl_err_cnt2); RTW_PRINT("\n"); for (i = 0; i < pstapriv->max_num_sta; i++) { psta = pstapriv->sta_aid[i]; if (!psta) continue; RTW_PRINT("[%d] STA[%02x:%02x:%02x:%02x:%02x:%02x]\n", i, psta->phl_sta->mac_addr[0], psta->phl_sta->mac_addr[1], psta->phl_sta->mac_addr[2], psta->phl_sta->mac_addr[3], psta->phl_sta->mac_addr[4], psta->phl_sta->mac_addr[5]); RTW_PRINT("[txsc] cur_idx:%d\n", psta->txsc_cur_idx); RTW_PRINT("[txsc][core] txsc_path_slow:%d\n", psta->txsc_path_slow); RTW_PRINT("[txsc][core] txsc_path_ps:%d\n", psta->txsc_path_ps); RTW_PRINT("[txsc][core] txsc_cache_hit:%d\n", psta->txsc_cache_hit); RTW_PRINT("[txsc][core] txsc_cache_miss:%d\n", psta->txsc_cache_miss); RTW_PRINT("\n"); for (j = 0 ; j < CORE_TXSC_ENTRY_NUM; j++) { if (!psta->txsc_entry_cache[j].txsc_is_used) continue; RTW_PRINT(" [%d][txsc][core] txsc_core_hit:%d\n", j, psta->txsc_entry_cache[j].txsc_cache_hit); #ifdef CONFIG_PHL_TXSC RTW_PRINT(" [%d][txsc][phl] txsc_phl_hit:%d\n", j, psta->phl_sta->phl_txsc[j].txsc_cache_hit); #endif RTW_PRINT("\n"); } } } void txsc_dump_data(u8 *buf, u16 buf_len, const char *prefix) { int i = 0, j; RTW_PRINT("[txsc_dump] [%s (%uB)]@%p:\n", prefix, buf_len, buf); if (buf == NULL) { RTW_PRINT("[txsc_dump] NULL!\n"); return; } while (i < buf_len) { RTW_PRINT("[txsc_dump] %04X -", i); for (j = 0; (j < 4) && (i < buf_len); j++, i += 4) RTW_PRINT(" %02X %02X %02X %02X", buf[i], buf[i+1], buf[i+2], buf[i+3]); RTW_PRINT("\n"); } } #ifdef CONFIG_PCI_HCI void txsc_recycle_txreq_phyaddr(_adapter *padapter, struct rtw_xmit_req *txreq) { PPCI_DATA pci_data = dvobj_to_pci(padapter->dvobj); struct pci_dev *pdev = pci_data->ppcidev; struct rtw_pkt_buf_list *pkt_list = (struct rtw_pkt_buf_list *)txreq->pkt_list; dma_addr_t phy_addr = 0; /* only recycle pkt_list[1] = skb->data for SW TXSC */ pkt_list++; phy_addr = (pkt_list->phy_addr_l); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT phy_addr |= ((u64)pkt_list->phy_addr_h << 32); #endif pci_unmap_bus_addr(pdev, &phy_addr, pkt_list->length, PCI_DMA_TODEVICE); } void txsc_fill_txreq_phyaddr(_adapter *padapter, struct rtw_pkt_buf_list *pkt_list) { PPCI_DATA pci_data = dvobj_to_pci(padapter->dvobj); struct pci_dev *pdev = pci_data->ppcidev; dma_addr_t phy_addr = 0; pci_get_bus_addr(pdev, pkt_list->vir_addr, &phy_addr, pkt_list->length, PCI_DMA_TODEVICE); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT pkt_list->phy_addr_h = phy_addr >> 32; #else pkt_list->phy_addr_h = 0x0; #endif pkt_list->phy_addr_l = phy_addr & 0xFFFFFFFF; } #endif static void txsc_init_pkt_entry(_adapter *padapter, struct sk_buff *pskb, struct txsc_pkt_entry *txsc_pkt) { u8 priority = 0, i; txsc_pkt->step = TXSC_NONE; txsc_pkt->txsc_id = 0xff; txsc_pkt->ptxreq = NULL; for (i = 0; i < MAX_TXSC_SKB_NUM; i++) txsc_pkt->xmit_skb[i] = NULL; txsc_pkt->psta = NULL; txsc_pkt->xmit_skb[0] = pskb; txsc_pkt->skb_cnt = 1; priority = *(pskb->data + ETH_HLEN + 1); txsc_pkt->priority = tos_to_up(priority); } static void txsc_add_sc_check(_adapter *padapter, struct xmit_frame *pxframe, struct txsc_pkt_entry *txsc_pkt) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct pkt_attrib *pattrib = &pxframe->attrib; struct sta_info *psta = pxframe->attrib.psta; if (!pxmitpriv->txsc_enable) { if (psta->txsc_cache_num > 0) txsc_pkt->step = TXSC_SKIP; else txsc_pkt->step = TXSC_NONE; goto exit; } if (pxframe->attrib.nr_frags > 1 || pxframe->attrib.bswenc == 1) goto exit; if (txsc_pkt->step != TXSC_NONE) goto exit; if (pattrib->qos_en && pattrib->ampdu_en == 1 && pattrib->ether_type == ETH_P_IP && !IS_MCAST(pattrib->ra) && !pattrib->icmp_pkt && !pattrib->dhcp_pkt) { RTW_PRINT("[%s] sta[%02x] add eth_type=0x%x pkt to txsc\n", __func__, pattrib->psta->phl_sta->mac_addr[5], pattrib->ether_type); txsc_pkt->step = TXSC_ADD; } exit: return; } static u8 txsc_get_sc_entry(_adapter *padapter, struct sk_buff *pskb, struct txsc_pkt_entry *txsc_pkt) { struct sta_priv *pstapriv = &padapter->stapriv; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct sta_info *psta = NULL; u8 *ptxsc_ethdr = NULL; u8 i, res, da[6], offset, res2 = 0, sta_hit = 0; res = _FAIL; offset = 6; if (!pxmitpriv->txsc_enable) return res; if (pxmitpriv->ptxsc_sta_cached) { if (pxmitpriv->ptxsc_sta_cached->phl_sta) sta_hit = _rtw_memcmp(pxmitpriv->ptxsc_sta_cached->phl_sta->mac_addr, pskb->data, 6); if (sta_hit) psta = pxmitpriv->ptxsc_sta_cached; } if (!sta_hit) { _rtw_memcpy(da, pskb->data, 6); if (IS_MCAST(da)) { res = _FAIL; } else { if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) psta = rtw_get_stainfo(pstapriv, da); else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv)); if (!psta) res = _FAIL; } } if (!psta) { res = _FAIL; goto exit; } /* skip power saving mode */ if (psta->state & WIFI_SLEEP_STATE) { res = _FAIL; txsc_pkt->step = TXSC_SKIP; psta->txsc_path_ps++; goto exit; } if (psta->txsc_cache_num == 0) { res = _FAIL; goto exit; } pxmitpriv->ptxsc_sta_cached = psta; txsc_pkt->step = TXSC_NONE; ptxsc_ethdr = (u8 *)&psta->txsc_entry_cache[psta->txsc_cache_idx].txsc_ethdr; res2 = _rtw_memcmp((pskb->data + offset), (ptxsc_ethdr + offset), (ETH_HLEN - offset)); if (res2 && (pskb->len <= psta->txsc_entry_cache[psta->txsc_cache_idx].txsc_frag_len)) { txsc_pkt->step = TXSC_APPLY; txsc_pkt->psta = psta; txsc_pkt->txsc_id = psta->txsc_cache_idx; res = _SUCCESS; } else { for (i = 0; i < CORE_TXSC_ENTRY_NUM; i++) { if (i != psta->txsc_cache_idx && psta->txsc_entry_cache[i].txsc_is_used) { ptxsc_ethdr = (u8 *)&(psta->txsc_entry_cache[i].txsc_ethdr); if (_rtw_memcmp((pskb->data + offset), (ptxsc_ethdr + offset), (ETH_HLEN - offset)) && (pskb->len <= psta->txsc_entry_cache[i].txsc_frag_len)) { txsc_pkt->step = TXSC_APPLY; txsc_pkt->txsc_id = i; txsc_pkt->psta = psta; psta->txsc_cache_idx = i; res = _SUCCESS; break; } } } } if ((res == _SUCCESS) && (pxmitpriv->txsc_debug_mode == 1)) { txsc_pkt->step = TXSC_DEBUG; res = _FAIL; } exit: return res; } static void txsc_prepare_sc_entry(_adapter *padapter, struct xmit_frame *pxframe, struct txsc_pkt_entry *txsc_pkt) { struct sta_priv *pstapriv = &padapter->stapriv; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct sta_info *psta = pxframe->attrib.psta; struct pkt_attrib *pattrib = &pxframe->attrib; struct rtw_xmit_req *txreq = pxframe->phl_txreq; struct rtw_pkt_buf_list *pkt_list = (struct rtw_pkt_buf_list *)txreq->pkt_list; struct rtw_pkt_buf_list *ptxsc_pkt_list0 = NULL; struct rtw_t_meta_data *ptxsc_mdata = NULL; struct sk_buff *pskb = txsc_pkt->xmit_skb[0]; u8 i, idx; u8 *ptxsc_ethdr = NULL; u8 *ptxsc_wlhdr = NULL; u8 *ptxsc_wlhdr_len = NULL; if (!psta) { RTW_ERR("%s: fetal err, XF_STA = NULL, please check.\n", __func__); return; } if (txsc_pkt->step == TXSC_SKIP) { if (pxmitpriv->txsc_enable && psta->txsc_cache_num > 0) psta->txsc_cache_miss++; else psta->txsc_path_slow++; } else if (txsc_pkt->step == TXSC_NONE) { psta->txsc_path_slow++; } if (txsc_pkt->step != TXSC_ADD) return; idx = psta->txsc_cur_idx; ptxsc_ethdr = (u8 *)&psta->txsc_entry_cache[idx].txsc_ethdr; #ifdef USE_ONE_WLHDR ptxsc_wlhdr = psta->txsc_entry_cache[idx].txsc_wlhdr; #else ptxsc_wlhdr = (u8 *)&psta->txsc_entry_cache[idx].txsc_wlhdr; #endif ptxsc_pkt_list0 = &psta->txsc_entry_cache[idx].txsc_pkt_list0; ptxsc_mdata = &psta->txsc_entry_cache[idx].txsc_mdata; ptxsc_wlhdr_len = &psta->txsc_entry_cache[idx].txsc_wlhdr_len; _rtw_spinlock_bh(&pxmitpriv->txsc_lock); if (psta->txsc_entry_cache[idx].txsc_is_used == 1) RTW_PRINT("[CORE_TXSC] txsc entry is full, replace rentry[%d]\n", idx); /* ALLOC WLHDR in DMA addr */ #ifdef USE_ONE_WLHDR if (!ptxsc_wlhdr) { ptxsc_wlhdr = rtw_zmalloc(CORE_TXSC_WLHDR_SIZE); psta->txsc_entry_cache[idx].txsc_wlhdr = ptxsc_wlhdr; } #endif /* ETH HDR */ _rtw_memcpy(ptxsc_ethdr, pskb->data, ETH_HLEN); /* WLAN HDR + LLC */ _rtw_memcpy(ptxsc_wlhdr, pkt_list->vir_addr, pkt_list->length); *ptxsc_wlhdr_len = pkt_list->length; /* pkt_list[0] */ ptxsc_pkt_list0->vir_addr = ptxsc_wlhdr; ptxsc_pkt_list0->length = pkt_list->length; #ifdef CONFIG_PCI_HCI txsc_fill_txreq_phyaddr(padapter, ptxsc_pkt_list0); #endif /* META DATA */ _rtw_memcpy(ptxsc_mdata, &txreq->mdata, sizeof(*ptxsc_mdata)); /* FRAGE_LEN */ psta->txsc_entry_cache[idx].txsc_frag_len = pxframe->attrib.frag_len_txsc; psta->txsc_entry_cache[idx].txsc_is_used = 1; psta->txsc_cache_idx = idx; psta->txsc_cur_idx = (psta->txsc_cur_idx + 1) % CORE_TXSC_ENTRY_NUM; if (psta->txsc_cache_num < CORE_TXSC_ENTRY_NUM) psta->txsc_cache_num++; psta->txsc_path_slow++; pxmitpriv->ptxsc_sta_cached = psta; txreq->treq_type = RTW_PHL_TREQ_TYPE_PHL_ADD_TXSC | RTW_PHL_TREQ_TYPE_PHL_UPDATE_TXSC; /* set shortcut id */ txreq->shortcut_id = idx; psta->txsc_entry_cache[idx].txsc_phl_id = idx; /* disable phl txsc */ if (!(pxmitpriv->txsc_debug_mask&BIT1)) txreq->shortcut_id = 0; RTW_PRINT("[CORE_TXSC][ADD] core_txsc_idx:%d(cur_idx:%d), txreq_sc_id:%d, txsc_frag_len:%d\n", idx, psta->txsc_cur_idx, txreq->shortcut_id, psta->txsc_entry_cache[idx].txsc_frag_len); /* for debug */ _print_txreq_mdata(ptxsc_mdata, __func__); _rtw_spinunlock_bh(&pxmitpriv->txsc_lock); } u8 txsc_get_sc_cached_entry(_adapter *padapter, struct sk_buff *pskb, struct txsc_pkt_entry *txsc_pkt) { txsc_init_pkt_entry(padapter, pskb, txsc_pkt); return txsc_get_sc_entry(padapter, pskb, txsc_pkt); } void txsc_add_sc_cache_entry(_adapter *padapter, struct xmit_frame *pxframe, struct txsc_pkt_entry *txsc_pkt) { txsc_add_sc_check(padapter, pxframe, txsc_pkt); txsc_prepare_sc_entry(padapter, pxframe, txsc_pkt); } u8 txsc_apply_sc_cached_entry(_adapter *padapter, struct txsc_pkt_entry *txsc_pkt) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct rtw_pkt_buf_list *pkt_list = NULL; struct rtw_pkt_buf_list *pkt_list0 = NULL; struct rtw_t_meta_data *mdata = NULL; struct rtw_xmit_req *txreq = NULL; struct xmit_txreq_buf *txreq_buf = NULL; struct txsc_entry *txsc; struct sta_info *psta = txsc_pkt->psta; struct sk_buff *xmit_skb[MAX_TXSC_SKB_NUM]; u8 *hdr = NULL; u16 *qc; int wlhdr_copy_len, payload_offset = ETH_HLEN; u8 idx, priority = 0, res = _SUCCESS; u8 i, skb_cnt = 0, is_hdr_need_update = 0, txsc_wlhdr_len; #ifndef USE_ONE_WLHDR u8 *head, *ptxsc_wlhdr; #endif if (!pxmitpriv->txsc_enable || !psta) return _SUCCESS; if (txsc_pkt->step != TXSC_APPLY && txsc_pkt->step != TXSC_AMSDU_APPLY && txsc_pkt->step != TXSC_DEBUG) return _SUCCESS; _rtw_memset(xmit_skb, 0x0, sizeof(xmit_skb)); /* get cached entry */ idx = txsc_pkt->txsc_id; txsc = &psta->txsc_entry_cache[idx]; pkt_list0 = &txsc->txsc_pkt_list0; mdata = &txsc->txsc_mdata; #ifdef USE_ONE_WLHDR txsc_wlhdr_len = pkt_list0->length; #else ptxsc_wlhdr = (u8 *)txsc->txsc_wlhdr; txsc_wlhdr_len = txsc->txsc_wlhdr_len; #endif if (txsc_pkt->step == TXSC_DEBUG) { _rtw_memset(psta->debug_buf, 0x0, (sizeof(struct rtw_xmit_req)+sizeof(struct rtw_pkt_buf_list)*2)); txreq = (struct rtw_xmit_req *)psta->debug_buf; pkt_list = (struct rtw_pkt_buf_list *)psta->debug_buf + sizeof(struct rtw_xmit_req); } else { #ifdef USE_ONE_WLHDR txreq_buf = (struct xmit_txreq_buf *)get_txreq_buffer(padapter, (u8 **)&txreq, (u8 **)&pkt_list, NULL, NULL); #else txreq_buf = (struct xmit_txreq_buf *)get_txreq_buffer(padapter, (u8 **)&txreq, (u8 **)&pkt_list, (u8 **)&head, NULL); #endif if (txreq_buf == NULL) { res = _FAIL; goto exit; } } /* init txreq buf */ txreq_buf->adapter = padapter; /* fill txreq other */ txreq->os_priv = (void *)txreq_buf; txreq->pkt_list = (u8 *)pkt_list; txreq->pkt_cnt = 0; txreq->total_len = 0; /* fill txsc pkt entry */ txsc_pkt->ptxreq = txreq; { /* for no tx_amsdu case */ xmit_skb[0] = txsc_pkt->xmit_skb[0]; skb_cnt = txsc_pkt->skb_cnt; } if (skb_cnt == 0) RTW_PRINT("[ERR][%s:%d] skb_cnt = 0 is a fatel error, plz check\n", __func__, __LINE__); /* fill_txreq_mdata */ _rtw_memcpy(&txreq->mdata, mdata, sizeof(txreq->mdata)); /* Update TID from IP header */ /* priority = *(xmit_skb[0]->data + ETH_HLEN + 1); */ /*txreq->mdata.tid = tos_to_up(priority); */ priority = txreq->mdata.tid = txsc_pkt->priority; /* SW shortcut --- */ /* rtw_core_wlan_fill_head */ /* use swseq in amsdu */ if (txreq->mdata.hw_seq_mode == 0) { /* generate sw seq */ //priority = txreq->mdata.tid; psta->sta_xmitpriv.txseq_tid[priority]++; psta->sta_xmitpriv.txseq_tid[priority] &= 0xFFF; txreq->mdata.sw_seq = psta->sta_xmitpriv.txseq_tid[priority]; hdr = txsc->txsc_wlhdr; SetSeqNum(hdr, txreq->mdata.sw_seq); is_hdr_need_update = 1; } wlhdr_copy_len = txsc_wlhdr_len; /* WLAN header from cache */ #ifdef USE_ONE_WLHDR _rtw_memcpy(pkt_list, pkt_list0, sizeof(struct rtw_pkt_buf_list)); #else #ifdef USE_PREV_WLHDR_BUF if (txreq_buf->macid == txreq->mdata.macid && txreq_buf->txsc_id == idx) { if (is_hdr_need_update) SetSeqNum(head, txreq->mdata.sw_seq);/* set sw seq */ } else #endif /* USE_PREV_WLHDR_BUF */ { _rtw_memcpy(head, ptxsc_wlhdr, wlhdr_copy_len); } /* fill wlhdr in pkt_list[0] */ pkt_list->vir_addr = head; pkt_list->length = wlhdr_copy_len; #ifdef CONFIG_PCI_HCI txsc_fill_txreq_phyaddr(padapter, pkt_list); #endif #endif/* USE_ONE_WLHDR */ txreq->total_len += pkt_list->length; txreq->pkt_cnt++; #ifdef USE_PREV_WLHDR_BUF txreq_buf->macid = txreq->mdata.macid; txreq_buf->txsc_id = idx; #endif/* USE_PREV_WLHDR_BUF */ /* Payload w.o. ether header */ /* CONFIG_TXSC_AMSDU for multiple skb */ for (i = 0; i < skb_cnt; i++) { pkt_list++; pkt_list->vir_addr = xmit_skb[i]->data + payload_offset; pkt_list->length = xmit_skb[i]->len - payload_offset; txreq->total_len += pkt_list->length; txreq->pkt_cnt++; #ifdef CONFIG_PCI_HCI txsc_fill_txreq_phyaddr(padapter, pkt_list); #endif txreq_buf->pkt[i] = (u8 *)xmit_skb[i]; } txreq->treq_type = RTW_PHL_TREQ_TYPE_CORE_TXSC; if (txsc_pkt->step == TXSC_APPLY) { psta->txsc_cache_hit++; txsc->txsc_cache_hit++; } else psta->txsc_path_slow++; /* SW shortcut --- */ txreq_buf->pkt_cnt = skb_cnt;/* for recycle multiple skb */ txreq->mdata.pktlen = txreq->total_len; txreq->shortcut_id = psta->txsc_entry_cache[idx].txsc_phl_id; if (!(pxmitpriv->txsc_debug_mask&BIT1)) txreq->shortcut_id = 0; /* send addbareq */ txsc_issue_addbareq_cmd(padapter, priority, psta, _TRUE); #ifdef RTW_PHL_DBG_CMD /* Update force rate settings so force rate takes effects * after shortcut cached */ if (padapter->txForce_enable) { if (padapter->txForce_rate != INV_TXFORCE_VAL) { txreq->mdata.f_rate = padapter->txForce_rate; txreq->mdata.userate_sel = 1; txreq->mdata.dis_data_rate_fb = 1; txreq->mdata.dis_rts_rate_fb = 1; } if (padapter->txForce_agg != INV_TXFORCE_VAL) txreq->mdata.ampdu_en = padapter->txForce_agg; if (padapter->txForce_aggnum != INV_TXFORCE_VAL) txreq->mdata.max_agg_num = padapter->txForce_aggnum; if (padapter->txForce_gi != INV_TXFORCE_VAL) txreq->mdata.f_gi_ltf = padapter->txForce_gi; if (padapter->txForce_ampdu_density != INV_TXFORCE_VAL) txreq->mdata.ampdu_density = padapter->txForce_ampdu_density; if (padapter->txForce_bw != INV_TXFORCE_VAL) txreq->mdata.f_bw = padapter->txForce_bw; } #endif /* RTW_PHL_DBG_CMD */ /* for tx debug */ _print_txreq_mdata(&txreq->mdata, __func__); _print_txreq_pklist(NULL, txsc_pkt->ptxreq, xmit_skb[0], __func__); exit: return res; } void txsc_free_txreq(_adapter *padapter, struct rtw_xmit_req *txreq) { struct xmit_txreq_buf *ptxreq_buf = NULL; struct sk_buff *tx_skb = NULL; _queue *queue = NULL; u8 i; if (txreq != NULL) ptxreq_buf = txreq->os_priv; else return; #ifdef CONFIG_PCI_HCI #ifdef USE_ONE_WLHDR txsc_recycle_txreq_phyaddr(padapter, txreq); #else core_recycle_txreq_phyaddr(padapter, txreq); #endif #endif if (!ptxreq_buf) { RTW_ERR("%s: NULL ptxreq_buf !!\n", __func__); rtw_warn_on(1); return; } queue = &padapter->free_txreq_queue; _rtw_spinlock_bh(&queue->lock); rtw_list_delete(&ptxreq_buf->list); rtw_list_insert_tail(&ptxreq_buf->list, get_list_head(queue)); txreq->os_priv = NULL; txreq->pkt_list = NULL; txreq->treq_type = RTW_PHL_TREQ_TYPE_NORMAL; /* this must be protected in spinlock section */ if (ptxreq_buf->pkt_cnt == 0) RTW_PRINT("[ERR][%s:%d] pkt_cnt = 0 is a fatel error, plz check\n", __func__, __LINE__); for (i = 0; i < ptxreq_buf->pkt_cnt; i++) { tx_skb = (struct sk_buff *)ptxreq_buf->pkt[i]; if (tx_skb) rtw_os_pkt_complete(padapter, tx_skb); else RTW_DBG("%s:tx recyele: tx_skb=NULL\n", __func__); ptxreq_buf->pkt[i] = NULL; } padapter->free_txreq_cnt++; _rtw_spinunlock_bh(&queue->lock); } void txsc_debug_sc_entry(_adapter *padapter, struct xmit_frame *pxframe, struct txsc_pkt_entry *txsc_pkt) { struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct rtw_pkt_buf_list *pkt_list = NULL; struct rtw_pkt_buf_list *txsc_pkt_list = NULL; struct rtw_xmit_req *txreq = pxframe->phl_txreq; struct rtw_xmit_req *ptxsc_txreq = txsc_pkt->ptxreq; u8 i; if (txsc_pkt->step != TXSC_DEBUG) return; if (pxmitpriv->txsc_debug_mask&BIT2) { RTW_PRINT("\n\nNormal Path TXREQ: %p\n\n", txreq); txsc_dump_data((u8 *)txreq, sizeof(struct rtw_xmit_req), "txreq:"); RTW_PRINT("os_priv=%p\n", txreq->os_priv); RTW_PRINT("treq_type=%d shortcut_id=%d\n", txreq->treq_type, txreq->shortcut_id); RTW_PRINT("total_len=%d pkt_cnt=%d\n", txreq->total_len, txreq->pkt_cnt); pkt_list = (struct rtw_pkt_buf_list *)txreq->pkt_list; for (i = 0; i < txreq->pkt_cnt; i++) { RTW_PRINT("[%d]", i); txsc_dump_data((u8 *)pkt_list, sizeof(struct rtw_pkt_buf_list), "pklist"); txsc_dump_data((u8 *)pkt_list->vir_addr, pkt_list->length, "pkt_list->vir_addr"); pkt_list++; } RTW_PRINT("mdata: pktlen=%d sw_seq=%d\n", txreq->mdata.pktlen, txreq->mdata.sw_seq); RTW_PRINT("\n\nShortcut Path TXREQ: %p\n\n", ptxsc_txreq); txsc_dump_data((u8 *)ptxsc_txreq, sizeof(struct rtw_xmit_req), "ptxsc_txreq:"); RTW_PRINT("os_priv=%p\n", ptxsc_txreq->os_priv); RTW_PRINT("treq_type=%d shortcut_id=%d\n", ptxsc_txreq->treq_type, ptxsc_txreq->shortcut_id); RTW_PRINT("total_len=%d pkt_cnt=%d\n", ptxsc_txreq->total_len, ptxsc_txreq->pkt_cnt); txsc_pkt_list = (struct rtw_pkt_buf_list *)ptxsc_txreq->pkt_list; for (i = 0; i < txreq->pkt_cnt; i++) { RTW_PRINT("[%d]", i); txsc_dump_data((u8 *)txsc_pkt_list, sizeof(struct rtw_pkt_buf_list), "pklist"); txsc_dump_data((u8 *)txsc_pkt_list->vir_addr, txsc_pkt_list->length, "pkt_list->vir_addr"); txsc_pkt_list++; } RTW_PRINT("mdata: pktlen=%d sw_seq=%d\n", ptxsc_txreq->mdata.pktlen, ptxsc_txreq->mdata.sw_seq); } else { if (!_rtw_memcmp(&txreq->mdata, &ptxsc_txreq->mdata, sizeof(struct rtw_t_meta_data))) { txsc_dump_data((u8 *)&txreq->mdata, sizeof(struct rtw_t_meta_data), "txreq->mdata"); txsc_dump_data((u8 *)&ptxsc_txreq->mdata, sizeof(struct rtw_t_meta_data), "ptxsc_txreq->mdata"); } pkt_list = (struct rtw_pkt_buf_list *)txreq->pkt_list; txsc_pkt_list = (struct rtw_pkt_buf_list *)ptxsc_txreq->pkt_list; if (pkt_list->length != txsc_pkt_list->length) { txsc_dump_data((u8 *)pkt_list, sizeof(struct rtw_pkt_buf_list), "pkt_list[0]"); txsc_dump_data((u8 *)txsc_pkt_list, sizeof(struct rtw_pkt_buf_list), "txsc_pkt_list[0]"); txsc_dump_data((u8 *)pkt_list->vir_addr, pkt_list->length, "pkt_list[0]->vir_addr"); txsc_dump_data(txsc_pkt_list->vir_addr, pkt_list->length, "txsc_pkt_list[0]->vir_addr"); } else if (pkt_list->length == txsc_pkt_list->length) { if (!_rtw_memcmp(pkt_list->vir_addr, txsc_pkt_list->vir_addr, pkt_list->length)) { txsc_dump_data((u8 *)pkt_list->vir_addr, pkt_list->length, "pkt_list[0]->vir_addr"); txsc_dump_data((u8 *)txsc_pkt_list->vir_addr, pkt_list->length, "txsc_pkt_list[0]->vir_addr"); } } } /* DO NOT WD CACHE */ txreq->shortcut_id = 0; txreq->treq_type = RTW_PHL_TREQ_TYPE_NORMAL; } #ifdef CONFIG_80211N_HT static u8 txsc_issue_addbareq_check(_adapter *padapter, u8 issue_when_busy) { struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct registry_priv *pregistry = &padapter->registrypriv; if (pregistry->tx_quick_addba_req == 0) { if ((issue_when_busy == _TRUE) && (pmlmepriv->LinkDetectInfo.bBusyTraffic == _FALSE)) return _FALSE; if (pmlmepriv->LinkDetectInfo.NumTxOkInPeriod < 100) return _FALSE; } return _TRUE; } void txsc_issue_addbareq_cmd(_adapter *padapter, u8 priority, struct sta_info *psta, u8 issue_when_busy) { u8 issued; struct ht_priv *phtpriv; if (txsc_issue_addbareq_check(padapter, issue_when_busy) == _FALSE) return; phtpriv = &psta->htpriv; if ((phtpriv->ht_option == _TRUE) && (phtpriv->ampdu_enable == _TRUE)) { issued = (phtpriv->agg_enable_bitmap >> priority) & 0x1; issued |= (phtpriv->candidate_tid_bitmap >> priority) & 0x1; if (issued == 0) { RTW_INFO("rtw_issue_addbareq_cmd, p=%d\n", priority); psta->htpriv.candidate_tid_bitmap |= BIT((u8)priority); rtw_addbareq_cmd(padapter, (u8) priority, psta->phl_sta->mac_addr); } } } #endif /* CONFIG_80211N_HT */ #endif /* CONFIG_CORE_TXSC */