18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * NXP Wireless LAN device driver: station TX data handling 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2011-2020 NXP 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by NXP 78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License Version 2, June 1991 88c2ecf20Sopenharmony_ci * (the "License"). You may use, redistribute and/or modify this File in 98c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which 108c2ecf20Sopenharmony_ci * is available by writing to the Free Software Foundation, Inc., 118c2ecf20Sopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 128c2ecf20Sopenharmony_ci * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 158c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 168c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 178c2ecf20Sopenharmony_ci * this warranty disclaimer. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "decl.h" 218c2ecf20Sopenharmony_ci#include "ioctl.h" 228c2ecf20Sopenharmony_ci#include "util.h" 238c2ecf20Sopenharmony_ci#include "fw.h" 248c2ecf20Sopenharmony_ci#include "main.h" 258c2ecf20Sopenharmony_ci#include "wmm.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * This function fills the TxPD for tx packets. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * The Tx buffer received by this function should already have the 318c2ecf20Sopenharmony_ci * header space allocated for TxPD. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * This function inserts the TxPD in between interface header and actual 348c2ecf20Sopenharmony_ci * data and adjusts the buffer pointers accordingly. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * The following TxPD fields are set by this function, as required - 378c2ecf20Sopenharmony_ci * - BSS number 388c2ecf20Sopenharmony_ci * - Tx packet length and offset 398c2ecf20Sopenharmony_ci * - Priority 408c2ecf20Sopenharmony_ci * - Packet delay 418c2ecf20Sopenharmony_ci * - Priority specific Tx control 428c2ecf20Sopenharmony_ci * - Flags 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_civoid *mwifiex_process_sta_txpd(struct mwifiex_private *priv, 458c2ecf20Sopenharmony_ci struct sk_buff *skb) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 488c2ecf20Sopenharmony_ci struct txpd *local_tx_pd; 498c2ecf20Sopenharmony_ci struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); 508c2ecf20Sopenharmony_ci unsigned int pad; 518c2ecf20Sopenharmony_ci u16 pkt_type, pkt_offset; 528c2ecf20Sopenharmony_ci int hroom = adapter->intf_hdr_len; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!skb->len) { 558c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 568c2ecf20Sopenharmony_ci "Tx: bad packet length: %d\n", skb->len); 578c2ecf20Sopenharmony_ci tx_info->status_code = -1; 588c2ecf20Sopenharmony_ci return skb->data; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci pad = ((void *)skb->data - (sizeof(*local_tx_pd) + hroom)- 668c2ecf20Sopenharmony_ci NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); 678c2ecf20Sopenharmony_ci skb_push(skb, sizeof(*local_tx_pd) + pad); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci local_tx_pd = (struct txpd *) skb->data; 708c2ecf20Sopenharmony_ci memset(local_tx_pd, 0, sizeof(struct txpd)); 718c2ecf20Sopenharmony_ci local_tx_pd->bss_num = priv->bss_num; 728c2ecf20Sopenharmony_ci local_tx_pd->bss_type = priv->bss_type; 738c2ecf20Sopenharmony_ci local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len - 748c2ecf20Sopenharmony_ci (sizeof(struct txpd) + 758c2ecf20Sopenharmony_ci pad))); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci local_tx_pd->priority = (u8) skb->priority; 788c2ecf20Sopenharmony_ci local_tx_pd->pkt_delay_2ms = 798c2ecf20Sopenharmony_ci mwifiex_wmm_compute_drv_pkt_delay(priv, skb); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS || 828c2ecf20Sopenharmony_ci tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) { 838c2ecf20Sopenharmony_ci local_tx_pd->tx_token_id = tx_info->ack_frame_id; 848c2ecf20Sopenharmony_ci local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (local_tx_pd->priority < 888c2ecf20Sopenharmony_ci ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) 898c2ecf20Sopenharmony_ci /* 908c2ecf20Sopenharmony_ci * Set the priority specific tx_control field, setting of 0 will 918c2ecf20Sopenharmony_ci * cause the default value to be used later in this function 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci local_tx_pd->tx_control = 948c2ecf20Sopenharmony_ci cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd-> 958c2ecf20Sopenharmony_ci priority]); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (adapter->pps_uapsd_mode) { 988c2ecf20Sopenharmony_ci if (mwifiex_check_last_packet_indication(priv)) { 998c2ecf20Sopenharmony_ci adapter->tx_lock_flag = true; 1008c2ecf20Sopenharmony_ci local_tx_pd->flags = 1018c2ecf20Sopenharmony_ci MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) 1068c2ecf20Sopenharmony_ci local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Offset of actual data */ 1098c2ecf20Sopenharmony_ci pkt_offset = sizeof(struct txpd) + pad; 1108c2ecf20Sopenharmony_ci if (pkt_type == PKT_TYPE_MGMT) { 1118c2ecf20Sopenharmony_ci /* Set the packet type and add header for management frame */ 1128c2ecf20Sopenharmony_ci local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type); 1138c2ecf20Sopenharmony_ci pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* make space for adapter->intf_hdr_len */ 1198c2ecf20Sopenharmony_ci skb_push(skb, hroom); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (!local_tx_pd->tx_control) 1228c2ecf20Sopenharmony_ci /* TxCtrl set by user or default */ 1238c2ecf20Sopenharmony_ci local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return skb->data; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * This function tells firmware to send a NULL data packet. 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * The function creates a NULL data packet with TxPD and sends to the 1328c2ecf20Sopenharmony_ci * firmware for transmission, with highest priority setting. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ciint mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 1378c2ecf20Sopenharmony_ci struct txpd *local_tx_pd; 1388c2ecf20Sopenharmony_ci struct mwifiex_tx_param tx_param; 1398c2ecf20Sopenharmony_ci/* sizeof(struct txpd) + Interface specific header */ 1408c2ecf20Sopenharmony_ci#define NULL_PACKET_HDR 64 1418c2ecf20Sopenharmony_ci u32 data_len = NULL_PACKET_HDR; 1428c2ecf20Sopenharmony_ci struct sk_buff *skb; 1438c2ecf20Sopenharmony_ci int ret; 1448c2ecf20Sopenharmony_ci struct mwifiex_txinfo *tx_info = NULL; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags)) 1478c2ecf20Sopenharmony_ci return -1; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!priv->media_connected) 1508c2ecf20Sopenharmony_ci return -1; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (adapter->data_sent) 1538c2ecf20Sopenharmony_ci return -1; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (adapter->if_ops.is_port_ready && 1568c2ecf20Sopenharmony_ci !adapter->if_ops.is_port_ready(priv)) 1578c2ecf20Sopenharmony_ci return -1; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci skb = dev_alloc_skb(data_len); 1608c2ecf20Sopenharmony_ci if (!skb) 1618c2ecf20Sopenharmony_ci return -1; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci tx_info = MWIFIEX_SKB_TXCB(skb); 1648c2ecf20Sopenharmony_ci memset(tx_info, 0, sizeof(*tx_info)); 1658c2ecf20Sopenharmony_ci tx_info->bss_num = priv->bss_num; 1668c2ecf20Sopenharmony_ci tx_info->bss_type = priv->bss_type; 1678c2ecf20Sopenharmony_ci tx_info->pkt_len = data_len - 1688c2ecf20Sopenharmony_ci (sizeof(struct txpd) + adapter->intf_hdr_len); 1698c2ecf20Sopenharmony_ci skb_reserve(skb, sizeof(struct txpd) + adapter->intf_hdr_len); 1708c2ecf20Sopenharmony_ci skb_push(skb, sizeof(struct txpd)); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci local_tx_pd = (struct txpd *) skb->data; 1738c2ecf20Sopenharmony_ci local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); 1748c2ecf20Sopenharmony_ci local_tx_pd->flags = flags; 1758c2ecf20Sopenharmony_ci local_tx_pd->priority = WMM_HIGHEST_PRIORITY; 1768c2ecf20Sopenharmony_ci local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); 1778c2ecf20Sopenharmony_ci local_tx_pd->bss_num = priv->bss_num; 1788c2ecf20Sopenharmony_ci local_tx_pd->bss_type = priv->bss_type; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci skb_push(skb, adapter->intf_hdr_len); 1818c2ecf20Sopenharmony_ci if (adapter->iface_type == MWIFIEX_USB) { 1828c2ecf20Sopenharmony_ci ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, 1838c2ecf20Sopenharmony_ci skb, NULL); 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci tx_param.next_pkt_len = 0; 1868c2ecf20Sopenharmony_ci ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, 1878c2ecf20Sopenharmony_ci skb, &tx_param); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci switch (ret) { 1908c2ecf20Sopenharmony_ci case -EBUSY: 1918c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1928c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 1938c2ecf20Sopenharmony_ci "%s: host_to_card failed: ret=%d\n", 1948c2ecf20Sopenharmony_ci __func__, ret); 1958c2ecf20Sopenharmony_ci adapter->dbg.num_tx_host_to_card_failure++; 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci case -1: 1988c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1998c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 2008c2ecf20Sopenharmony_ci "%s: host_to_card failed: ret=%d\n", 2018c2ecf20Sopenharmony_ci __func__, ret); 2028c2ecf20Sopenharmony_ci adapter->dbg.num_tx_host_to_card_failure++; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case 0: 2058c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2068c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 2078c2ecf20Sopenharmony_ci "data: %s: host_to_card succeeded\n", 2088c2ecf20Sopenharmony_ci __func__); 2098c2ecf20Sopenharmony_ci adapter->tx_lock_flag = true; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci case -EINPROGRESS: 2128c2ecf20Sopenharmony_ci adapter->tx_lock_flag = true; 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci default: 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return ret; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* 2228c2ecf20Sopenharmony_ci * This function checks if we need to send last packet indication. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ciu8 2258c2ecf20Sopenharmony_cimwifiex_check_last_packet_indication(struct mwifiex_private *priv) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 2288c2ecf20Sopenharmony_ci u8 ret = false; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!adapter->sleep_period.period) 2318c2ecf20Sopenharmony_ci return ret; 2328c2ecf20Sopenharmony_ci if (mwifiex_wmm_lists_empty(adapter)) 2338c2ecf20Sopenharmony_ci ret = true; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (ret && !adapter->cmd_sent && !adapter->curr_cmd && 2368c2ecf20Sopenharmony_ci !is_command_pending(adapter)) { 2378c2ecf20Sopenharmony_ci adapter->delay_null_pkt = false; 2388c2ecf20Sopenharmony_ci ret = true; 2398c2ecf20Sopenharmony_ci } else { 2408c2ecf20Sopenharmony_ci ret = false; 2418c2ecf20Sopenharmony_ci adapter->delay_null_pkt = true; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci} 245