| /** @file mlan_11n_aggr.c |
| * |
| * @brief This file contains functions for 11n Aggregation. |
| * |
| * |
| * Copyright 2008-2021 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 (mbuf->flags & MLAN_BUF_FLAG_TDLS) |
| ptx_pd->flags = MRVDRV_TxPD_FLAGS_TDLS_PACKET; |
| 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 }; |
| t_u8 tdls_action_type[2] = { 0x89, 0x0d }; |
| t_u32 in_ts_sec, in_ts_usec; |
| t_u32 out_ts_sec, out_ts_usec; |
| t_u32 in_copy_ts_sec, in_copy_ts_usec; |
| t_u32 out_copy_ts_sec, out_copy_ts_usec; |
| t_u32 copy_delay = 0; |
| t_u32 delay = 0; |
| |
| 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 > (int)max_rx_data_size) { |
| PRINTM(MERROR, |
| "Total packet length greater than tx buffer" |
| " size %d\n", total_pkt_len); |
| goto done; |
| } |
| if (pmadapter->tp_state_on) |
| pmadapter->callbacks.moal_get_system_time(pmadapter-> |
| pmoal_handle, |
| &in_ts_sec, |
| &in_ts_usec); |
| 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 */ ); |
| pmadapter->callbacks.moal_tp_accounting_rx_param(pmadapter-> |
| pmoal_handle, |
| 4, |
| pmbuf-> |
| use_count); |
| } |
| if (pmadapter->tp_state_drop_point == 7 /*RX_DROP_P3 */ ) |
| goto done; |
| prx_pkt = (RxPacketHdr_t *)data; |
| if (pmbuf->pdesc && !memcmp(pmadapter, prx_pkt->eth803_hdr.dest_addr, |
| priv->curr_addr, MLAN_MAC_ADDR_LENGTH)) { |
| if (pmadapter->callbacks.moal_recv_amsdu_packet) { |
| ret = pmadapter->callbacks. |
| moal_recv_amsdu_packet(pmadapter->pmoal_handle, |
| pmbuf); |
| if (ret == MLAN_STATUS_PENDING) { |
| priv->msdu_in_rx_amsdu_cnt += pmbuf->use_count; |
| priv->amsdu_rx_cnt++; |
| return ret; |
| } |
| 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->extra_ts_sec = pmbuf->extra_ts_sec; |
| daggr_mbuf->extra_ts_usec = pmbuf->extra_ts_usec; |
| daggr_mbuf->pparent = pmbuf; |
| daggr_mbuf->priority = pmbuf->priority; |
| if (pmadapter->tp_state_on) |
| pmadapter->callbacks.moal_get_system_time(pmadapter-> |
| pmoal_handle, |
| &in_copy_ts_sec, |
| &in_copy_ts_usec); |
| memcpy_ext(pmadapter, |
| daggr_mbuf->pbuf + daggr_mbuf->data_offset, data, |
| pkt_len, daggr_mbuf->data_len); |
| if (pmadapter->tp_state_on) { |
| pmadapter->callbacks.moal_get_system_time(pmadapter-> |
| pmoal_handle, |
| &out_copy_ts_sec, |
| &out_copy_ts_usec); |
| copy_delay += |
| (t_s32)(out_copy_ts_sec - |
| in_copy_ts_sec) * 1000000; |
| copy_delay += |
| (t_s32)(out_copy_ts_usec - in_copy_ts_usec); |
| } |
| #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; |
| } |
| /**process tdls packet*/ |
| if (!memcmp(pmadapter, |
| daggr_mbuf->pbuf + daggr_mbuf->data_offset + |
| MLAN_ETHER_PKT_TYPE_OFFSET, |
| tdls_action_type, |
| sizeof(tdls_action_type))) { |
| PRINTM(MEVENT, |
| "Recevie AMSDU TDLS action frame\n"); |
| wlan_process_tdls_action_frame(priv, |
| daggr_mbuf-> |
| pbuf + |
| daggr_mbuf-> |
| data_offset, |
| daggr_mbuf-> |
| data_len); |
| } |
| |
| 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; |
| } |
| if (pmadapter->tp_state_on) { |
| pmadapter->callbacks.moal_get_system_time(pmadapter-> |
| pmoal_handle, |
| &out_ts_sec, |
| &out_ts_usec); |
| delay += (t_s32)(out_ts_sec - in_ts_sec) * 1000000; |
| delay += (t_s32)(out_ts_usec - in_ts_usec); |
| pmadapter->callbacks.moal_amsdu_tp_accounting(pmadapter-> |
| pmoal_handle, |
| delay, |
| copy_delay); |
| } |
| |
| 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); |
| t_u32 msdu_in_tx_amsdu_cnt = 0; |
| 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_TDLS) |
| pmbuf_aggr->flags |= MLAN_BUF_FLAG_TDLS; |
| 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++; |
| 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); |
| pmadapter->callbacks.moal_tp_accounting_rx_param(pmadapter-> |
| pmoal_handle, |
| 5, |
| msdu_in_tx_amsdu_cnt); |
| } |
| |
| /* 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; |
| } |