diff options
Diffstat (limited to 'drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.c')
-rw-r--r-- | drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.c | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.c b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.c new file mode 100644 index 000000000000..17cd78d02eb0 --- /dev/null +++ b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.c @@ -0,0 +1,603 @@ +/** @file mlan_11n_aggr.c + * + * @brief This file contains functions for 11n Aggregation. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 11/10/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11n_aggr.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Aggregate individual packets into one AMSDU packet + * + * @param pmadapter A pointer to mlan_adapter structure + * @param amsdu_buf A pointer to packet buffer + * @param data A pointer to aggregated data packet being formed + * @param pkt_len Length of current packet to aggregate + * @param pad Pad + * + * @return Final packet size + */ +static int wlan_11n_form_amsdu_pkt(pmlan_adapter pmadapter, t_u8 *amsdu_buf, + t_u8 *data, int pkt_len, int *pad) +{ + int dt_offset, amsdu_buf_offset; + Rfc1042Hdr_t snap = { + 0xaa, /* LLC DSAP */ + 0xaa, /* LLC SSAP */ + 0x03, /* LLC CTRL */ + {0x00, 0x00, 0x00}, /* SNAP OUI */ + 0x0000 /* SNAP type */ + /* + * This field will be overwritten + * later with ethertype + */ + }; + + ENTER(); + + memcpy_ext(pmadapter, amsdu_buf, data, (MLAN_MAC_ADDR_LENGTH)*2, + (MLAN_MAC_ADDR_LENGTH)*2); + dt_offset = amsdu_buf_offset = (MLAN_MAC_ADDR_LENGTH)*2; + + snap.snap_type = *(t_u16 *)(data + dt_offset); + dt_offset += sizeof(t_u16); + *(t_u16 *)(amsdu_buf + amsdu_buf_offset) = + mlan_htons(pkt_len + LLC_SNAP_LEN - + ((2 * MLAN_MAC_ADDR_LENGTH) + sizeof(t_u16))); + amsdu_buf_offset += sizeof(t_u16); + memcpy_ext(pmadapter, amsdu_buf + amsdu_buf_offset, &snap, LLC_SNAP_LEN, + LLC_SNAP_LEN); + amsdu_buf_offset += LLC_SNAP_LEN; + + memcpy_ext(pmadapter, amsdu_buf + amsdu_buf_offset, data + dt_offset, + pkt_len - dt_offset, pkt_len - dt_offset); + *pad = (((pkt_len + LLC_SNAP_LEN) & 3)) ? + (4 - (((pkt_len + LLC_SNAP_LEN)) & 3)) : + 0; + + LEAVE(); + return pkt_len + LLC_SNAP_LEN + *pad; +} + +/** + * @brief Add TxPD to AMSDU header + * + * @param priv A pointer to mlan_private structure + * @param mbuf Pointer to buffer where the TxPD will be formed + * + * @return N/A + */ +static void wlan_11n_form_amsdu_txpd(mlan_private *priv, mlan_buffer *mbuf) +{ + TxPD *ptx_pd; + mlan_adapter *pmadapter = priv->adapter; + + ENTER(); + + ptx_pd = (TxPD *)mbuf->pbuf; + memset(pmadapter, ptx_pd, 0, sizeof(TxPD)); + + /* + * Original priority has been overwritten + */ + ptx_pd->priority = (t_u8)mbuf->priority; + ptx_pd->pkt_delay_2ms = + wlan_wmm_compute_driver_packet_delay(priv, mbuf); + ptx_pd->bss_num = GET_BSS_NUM(priv); + ptx_pd->bss_type = priv->bss_type; + /* Always zero as the data is followed by TxPD */ + ptx_pd->tx_pkt_offset = sizeof(TxPD); + ptx_pd->tx_pkt_type = PKT_TYPE_AMSDU; + if (ptx_pd->tx_control == 0) + /* TxCtrl set by user or default */ + ptx_pd->tx_control = priv->pkt_tx_ctrl; + + endian_convert_TxPD(ptx_pd); + + LEAVE(); +} + +/** + * @brief Update the TxPktLength field in TxPD after the complete AMSDU + * packet is formed + * + * @param priv A pointer to mlan_private structure + * @param mbuf TxPD buffer + * + * @return N/A + */ +static INLINE void wlan_11n_update_pktlen_amsdu_txpd(mlan_private *priv, + pmlan_buffer mbuf) +{ + TxPD *ptx_pd; + ENTER(); + + ptx_pd = (TxPD *)mbuf->pbuf; + ptx_pd->tx_pkt_length = + (t_u16)wlan_cpu_to_le16(mbuf->data_len - sizeof(TxPD)); +#ifdef STA_SUPPORT + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (priv->adapter->pps_uapsd_mode)) { + if (MTRUE == wlan_check_last_packet_indication(priv)) { + priv->adapter->tx_lock_flag = MTRUE; + ptx_pd->flags |= MRVDRV_TxPD_POWER_MGMT_LAST_PACKET; + } + } +#endif /* STA_SUPPORT */ + LEAVE(); +} + +/** + * @brief Get number of aggregated packets + * + * @param data A pointer to packet data + * @param total_pkt_len Total packet length + * + * @return Number of packets + */ +static int wlan_11n_get_num_aggrpkts(t_u8 *data, int total_pkt_len) +{ + int pkt_count = 0, pkt_len, pad; + t_u8 hdr_len = sizeof(Eth803Hdr_t); + + ENTER(); + while (total_pkt_len >= hdr_len) { + /* Length will be in network format, change it to host */ + pkt_len = mlan_ntohs( + (*(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH)))); + if (pkt_len > total_pkt_len) { + PRINTM(MERROR, "Error in packet length.\n"); + break; + } + + pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ? + (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) : + 0; + data += pkt_len + pad + sizeof(Eth803Hdr_t); + total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t); + ++pkt_count; + } + LEAVE(); + return pkt_count; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Deaggregate the received AMSDU packet + * + * @param priv A pointer to mlan_private structure + * @param pmbuf A pointer to aggregated data packet + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_11n_deaggregate_pkt(mlan_private *priv, pmlan_buffer pmbuf) +{ + t_u16 pkt_len; + int total_pkt_len; + t_u8 *data; + mlan_adapter *pmadapter = priv->adapter; + t_u32 max_rx_data_size = MLAN_RX_DATA_BUF_SIZE; + int pad; + mlan_status ret = MLAN_STATUS_FAILURE; + RxPacketHdr_t *prx_pkt; + mlan_buffer *daggr_mbuf = MNULL; + t_u8 rfc1042_eth_hdr[MLAN_MAC_ADDR_LENGTH] = {0xaa, 0xaa, 0x03, + 0x00, 0x00, 0x00}; + t_u8 hdr_len = sizeof(Eth803Hdr_t); + t_u8 eapol_type[2] = {0x88, 0x8e}; + + ENTER(); + + data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + total_pkt_len = pmbuf->data_len; + + /* Sanity test */ +#if defined(USB) + if (IS_USB(pmadapter->card_type) && + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable) { + max_rx_data_size = + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_max; + if (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_mode == + MLAN_USB_AGGR_MODE_NUM) { + max_rx_data_size *= + MAX(MLAN_USB_MAX_PKT_SIZE, + pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_align); + max_rx_data_size = + MAX(max_rx_data_size, MLAN_RX_DATA_BUF_SIZE); + } + } +#endif + if (total_pkt_len > max_rx_data_size) { + PRINTM(MERROR, + "Total packet length greater than tx buffer" + " size %d\n", + total_pkt_len); + goto done; + } + + pmbuf->use_count = wlan_11n_get_num_aggrpkts(data, total_pkt_len); + + // rx_trace 7 + if (pmadapter->tp_state_on) + pmadapter->callbacks.moal_tp_accounting( + pmadapter->pmoal_handle, pmbuf, 7 /*RX_DROP_P3*/); + if (pmadapter->tp_state_drop_point == 7 /*RX_DROP_P3*/) + goto done; + + while (total_pkt_len >= hdr_len) { + prx_pkt = (RxPacketHdr_t *)data; + /* Length will be in network format, change it to host */ + pkt_len = mlan_ntohs( + (*(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH)))); + if (pkt_len > total_pkt_len) { + PRINTM(MERROR, + "Error in packet length: total_pkt_len = %d, pkt_len = %d\n", + total_pkt_len, pkt_len); + ret = MLAN_STATUS_FAILURE; + break; + } + + pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ? + (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) : + 0; + + total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t); + + if (memcmp(pmadapter, &prx_pkt->rfc1042_hdr, rfc1042_eth_hdr, + sizeof(rfc1042_eth_hdr)) == 0) { + memmove(pmadapter, data + LLC_SNAP_LEN, data, + (2 * MLAN_MAC_ADDR_LENGTH)); + data += LLC_SNAP_LEN; + pkt_len += sizeof(Eth803Hdr_t) - LLC_SNAP_LEN; + } else { + *(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH)) = + (t_u16)0; + pkt_len += sizeof(Eth803Hdr_t); + } + daggr_mbuf = wlan_alloc_mlan_buffer(pmadapter, + pkt_len + MLAN_NET_IP_ALIGN, + 0, MOAL_ALLOC_MLAN_BUFFER); + if (daggr_mbuf == MNULL) { + PRINTM(MERROR, "Error allocating daggr mlan_buffer\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + daggr_mbuf->data_offset += MLAN_NET_IP_ALIGN; + daggr_mbuf->bss_index = pmbuf->bss_index; + daggr_mbuf->buf_type = pmbuf->buf_type; + daggr_mbuf->data_len = pkt_len; + daggr_mbuf->in_ts_sec = pmbuf->in_ts_sec; + daggr_mbuf->in_ts_usec = pmbuf->in_ts_usec; + daggr_mbuf->pparent = pmbuf; + daggr_mbuf->priority = pmbuf->priority; + memcpy_ext(pmadapter, + daggr_mbuf->pbuf + daggr_mbuf->data_offset, data, + pkt_len, daggr_mbuf->data_len); + +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_uap_recv_packet(priv, daggr_mbuf); + } else { +#endif /* UAP_SUPPORT */ + /** send EAPOL from AMSDU pkt to firmware */ + if (priv->sec_info.ewpa_enabled && + (!memcmp(pmadapter, + daggr_mbuf->pbuf + + daggr_mbuf->data_offset + + MLAN_ETHER_PKT_TYPE_OFFSET, + eapol_type, sizeof(eapol_type)))) { + ret = wlan_prepare_cmd( + priv, HostCmd_CMD_802_11_EAPOL_PKT, 0, + 0, MNULL, daggr_mbuf); + if (ret == MLAN_STATUS_SUCCESS) + wlan_recv_event( + priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + wlan_free_mlan_buffer(pmadapter, daggr_mbuf); + data += pkt_len + pad; + continue; + } + + ret = pmadapter->callbacks.moal_recv_packet( + pmadapter->pmoal_handle, daggr_mbuf); +#ifdef UAP_SUPPORT + } +#endif /* UAP_SUPPORT */ + switch (ret) { + case MLAN_STATUS_PENDING: + break; + case MLAN_STATUS_FAILURE: + PRINTM(MERROR, "Deaggr, send to moal failed\n"); + daggr_mbuf->status_code = MLAN_ERROR_PKT_INVALID; + /* fall through */ + case MLAN_STATUS_SUCCESS: + wlan_recv_packet_complete(pmadapter, daggr_mbuf, ret); + break; + default: + break; + } + + data += pkt_len + pad; + } + +done: + priv->msdu_in_rx_amsdu_cnt += pmbuf->use_count; + priv->amsdu_rx_cnt++; + /** we should free the aggr buffer after deaggr */ + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + LEAVE(); + return ret; +} + +/** + * @brief Aggregate multiple packets into one single AMSDU packet + * + * @param priv A pointer to mlan_private structure + * @param pra_list Pointer to the RA List table containing the pointers + * to packets. + * @param headroom Any interface specific headroom that may be need. TxPD + * will be formed leaving this headroom. + * @param ptrindex Pointer index + * + * @return Final packet size or MLAN_STATUS_FAILURE + */ +int wlan_11n_aggregate_pkt(mlan_private *priv, raListTbl *pra_list, + int headroom, int ptrindex) +{ + int pkt_size = 0; + pmlan_adapter pmadapter = priv->adapter; + mlan_buffer *pmbuf_aggr, *pmbuf_src; + t_u8 *data; + int pad = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + mlan_tx_param tx_param; +#ifdef STA_SUPPORT + TxPD *ptx_pd = MNULL; +#endif + t_u32 max_amsdu_size = MIN(pra_list->max_amsdu, pmadapter->tx_buf_size); + ENTER(); + + PRINTM(MDAT_D, "Handling Aggr packet\n"); + + pmbuf_src = (pmlan_buffer)util_peek_list( + pmadapter->pmoal_handle, &pra_list->buf_head, MNULL, MNULL); + if (pmbuf_src) { + pmbuf_aggr = wlan_alloc_mlan_buffer(pmadapter, + pmadapter->tx_buf_size, 0, + MOAL_MALLOC_BUFFER); + if (!pmbuf_aggr) { + PRINTM(MERROR, "Error allocating mlan_buffer\n"); + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + data = pmbuf_aggr->pbuf + headroom; + pmbuf_aggr->bss_index = pmbuf_src->bss_index; + pmbuf_aggr->buf_type = pmbuf_src->buf_type; + pmbuf_aggr->priority = pmbuf_src->priority; + pmbuf_aggr->pbuf = data; + pmbuf_aggr->data_offset = 0; + pmbuf_aggr->in_ts_sec = pmbuf_src->in_ts_sec; + pmbuf_aggr->in_ts_usec = pmbuf_src->in_ts_usec; + if (pmbuf_src->flags & MLAN_BUF_FLAG_TCP_ACK) + pmbuf_aggr->flags |= MLAN_BUF_FLAG_TCP_ACK; + + /* Form AMSDU */ + wlan_11n_form_amsdu_txpd(priv, pmbuf_aggr); + pkt_size = sizeof(TxPD); +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + ptx_pd = (TxPD *)pmbuf_aggr->pbuf; +#endif + priv->msdu_in_tx_amsdu_cnt++; + } else { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + goto exit; + } + + while (pmbuf_src && ((pkt_size + (pmbuf_src->data_len + LLC_SNAP_LEN) + + headroom) <= max_amsdu_size)) { + pmbuf_src = + (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle, + &pra_list->buf_head, + MNULL, MNULL); + /* Collects TP statistics */ + if (pmadapter->tp_state_on && (pkt_size > sizeof(TxPD))) + pmadapter->callbacks.moal_tp_accounting( + pmadapter->pmoal_handle, pmbuf_src->pdesc, 3); + pra_list->total_pkts--; + + /* decrement for every PDU taken from the list */ + priv->wmm.pkts_queued[ptrindex]--; + util_scalar_decrement(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + + if (pmbuf_src) { + pkt_size += wlan_11n_form_amsdu_pkt( + pmadapter, (data + pkt_size), + pmbuf_src->pbuf + pmbuf_src->data_offset, + pmbuf_src->data_len, &pad); + + DBG_HEXDUMP(MDAT_D, "pmbuf_src", pmbuf_src, + sizeof(mlan_buffer)); + wlan_write_data_complete(pmadapter, pmbuf_src, + MLAN_STATUS_SUCCESS); + } + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pmbuf_src = + (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &pra_list->buf_head, MNULL, + MNULL); + priv->msdu_in_tx_amsdu_cnt++; + } + + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + /* Last AMSDU packet does not need padding */ + pkt_size -= pad; + pmbuf_aggr->data_len = pkt_size; + wlan_11n_update_pktlen_amsdu_txpd(priv, pmbuf_aggr); + pmbuf_aggr->data_len += headroom; + pmbuf_aggr->pbuf = data - headroom; + tx_param.next_pkt_len = + ((pmbuf_src) ? pmbuf_src->data_len + sizeof(TxPD) : 0); + /* Collects TP statistics */ + if (pmadapter->tp_state_on) + pmadapter->callbacks.moal_tp_accounting(pmadapter->pmoal_handle, + pmbuf_aggr, 4); + + /* Drop Tx packets at drop point 4 */ + if (pmadapter->tp_state_drop_point == 4) { + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + goto exit; + } else + ret = pmadapter->ops.host_to_card(priv, MLAN_TYPE_DATA, + pmbuf_aggr, &tx_param); + switch (ret) { +#ifdef USB + case MLAN_STATUS_PRESOURCE: + PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n"); + break; +#endif + case MLAN_STATUS_RESOURCE: + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + pmbuf_aggr->status_code = MLAN_ERROR_PKT_INVALID; + wlan_write_data_complete(pmadapter, pmbuf_aggr, + MLAN_STATUS_FAILURE); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#ifdef STA_SUPPORT + /* reset tx_lock_flag */ + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + pmadapter->pps_uapsd_mode && + (pmadapter->tx_lock_flag == MTRUE)) { + pmadapter->tx_lock_flag = MFALSE; + if (ptx_pd != MNULL) + ptx_pd->flags = 0; + } +#endif + util_enqueue_list_head(pmadapter->pmoal_handle, + &pra_list->buf_head, + (pmlan_linked_list)pmbuf_aggr, MNULL, + MNULL); + + pra_list->total_pkts++; + + /* add back only one: aggregated packet is requeued as one */ + priv->wmm.pkts_queued[ptrindex]++; + util_scalar_increment(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + pmbuf_aggr->flags |= MLAN_BUF_FLAG_REQUEUED_PKT; + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + PRINTM(MINFO, "MLAN_STATUS_RESOURCE is returned\n"); + pmbuf_aggr->status_code = MLAN_ERROR_PKT_INVALID; + break; + case MLAN_STATUS_FAILURE: + pmbuf_aggr->status_code = MLAN_ERROR_DATA_TX_FAIL; + pmadapter->dbg.num_tx_host_to_card_failure++; + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + goto exit; + case MLAN_STATUS_PENDING: + break; + case MLAN_STATUS_SUCCESS: + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + break; + default: + break; + } + if (ret != MLAN_STATUS_RESOURCE) { + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + if (wlan_is_ralist_valid(priv, pra_list, ptrindex)) { + priv->wmm.packets_out[ptrindex]++; + priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list; + } + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur = + pmadapter->bssprio_tbl[priv->bss_priority] + .bssprio_cur->pnext; + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + } + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec); + priv->amsdu_tx_cnt++; + +exit: + LEAVE(); + return pkt_size + headroom; +} |