blob: f1532303852eb1732b2b7c7698385d8028477288 [file] [log] [blame]
/** @file mlan_sta_tx.c
*
* @brief This file contains the handling of data packet
* transmission in MLAN module.
*
* Copyright (C) 2008-2009, Marvell International Ltd.
* All Rights Reserved
*/
/********************************************************
Change log:
10/21/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_sdio.h"
/********************************************************
Local Variables
********************************************************/
/********************************************************
Global Variables
********************************************************/
/********************************************************
Local Functions
********************************************************/
/********************************************************
Global functions
********************************************************/
/**
* @brief This function fill the pxpd for tx packet
*
* @param priv A pointer to mlan_private structure
* @param pmbuf A pointer to the mlan_buffer for process
*
* @return headptr or MNULL
*/
t_void *
mlan_process_sta_txpd(IN t_void * priv, IN pmlan_buffer pmbuf)
{
mlan_private *pmpriv = (mlan_private *) priv;
pmlan_adapter pmadapter = pmpriv->adapter;
TxPD *plocal_tx_pd;
t_u8 *head_ptr = MNULL;
if (!pmbuf->data_len) {
PRINTM(MERROR, "Tx Error: bad packet length: %d\n", pmbuf->data_len);
pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID;
goto done;
}
if (pmbuf->data_offset < (sizeof(TxPD) + INTF_HEADER_LEN +
HEADER_ALIGNMENT)) {
PRINTM(MERROR, "not enough space for TxPD: %d\n", pmbuf->data_len);
pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID;
goto done;
}
/* head_ptr should be aligned */
head_ptr =
pmbuf->pbuf + pmbuf->data_offset - sizeof(TxPD) - INTF_HEADER_LEN;
head_ptr = (t_u8 *) ((t_u32) head_ptr & ~((t_u32) (HEADER_ALIGNMENT - 1)));
plocal_tx_pd = (TxPD *) (head_ptr + INTF_HEADER_LEN);
memset(plocal_tx_pd, 0, sizeof(TxPD));
/* Set the BSS number to TxPD */
plocal_tx_pd->bss_num = pmpriv->bss_num;
plocal_tx_pd->tx_pkt_length = (t_u16) pmbuf->data_len;
plocal_tx_pd->priority = (t_u8) pmbuf->priority;
plocal_tx_pd->pkt_delay_2ms =
wlan_wmm_compute_driver_packet_delay(pmpriv, pmbuf);
if (plocal_tx_pd->priority < NELEMENTS(pmpriv->wmm.user_pri_pkt_tx_ctrl))
/*
* Set the priority specific tx_control field, setting of 0 will
* cause the default value to be used later in this function
*/
plocal_tx_pd->tx_control
= pmpriv->wmm.user_pri_pkt_tx_ctrl[plocal_tx_pd->priority];
if (pmadapter->ps_state != PS_STATE_AWAKE) {
if (MTRUE == wlan_check_last_packet_indication(pmpriv)) {
pmadapter->tx_lock_flag = MTRUE;
plocal_tx_pd->flags = MRVDRV_TxPD_POWER_MGMT_LAST_PACKET;
}
}
/* Offset of actual data */
plocal_tx_pd->tx_pkt_offset =
(t_u16) ((t_u32) pmbuf->pbuf + pmbuf->data_offset -
(t_u32) plocal_tx_pd);
if (!plocal_tx_pd->tx_control) {
/* TxCtrl set by user or default */
plocal_tx_pd->tx_control = pmpriv->pkt_tx_ctrl;
}
endian_convert_TxPD(plocal_tx_pd);
/* Adjust the data offset and length to include TxPD in pmbuf */
pmbuf->data_len += pmbuf->data_offset;
pmbuf->data_offset = head_ptr - pmbuf->pbuf;
pmbuf->data_len -= pmbuf->data_offset;
done:
return head_ptr;
}
/**
* @brief This function tells firmware to send a NULL data packet.
*
* @param priv A pointer to mlan_private structure
* @param flags Trasmit Pkt Flags
*
* @return N/A
*/
mlan_status
wlan_send_null_packet(pmlan_private priv, t_u8 flags)
{
pmlan_adapter pmadapter = priv->adapter;
TxPD *ptx_pd;
/* sizeof(TxPD) + Interface specific header */
#define NULL_PACKET_HDR 64
t_u32 data_len = NULL_PACKET_HDR + HEADER_ALIGNMENT;
pmlan_buffer pmbuf = MNULL;
t_u8 *ptr;
mlan_status ret = MLAN_STATUS_SUCCESS;
t_u32 sec, usec;
ENTER();
if (pmadapter->surprise_removed == MTRUE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (priv->media_connected == MFALSE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (pmadapter->data_sent == MTRUE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
pmbuf = wlan_alloc_mlan_buffer(&pmadapter->callbacks, data_len);
if (!pmbuf) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
memset(pmbuf->pbuf, 0, data_len);
pmbuf->bss_num = priv->bss_num;
pmbuf->buf_type = MLAN_BUF_TYPE_DATA;
ptr = (t_u8 *) ALIGN_ADDR(pmbuf->pbuf + pmbuf->data_offset,
HEADER_ALIGNMENT);
pmbuf->data_offset = ptr - pmbuf->pbuf;
pmbuf->data_len = sizeof(TxPD) + INTF_HEADER_LEN;
ptx_pd = (TxPD *) (ptr + INTF_HEADER_LEN);
ptx_pd->tx_control = priv->pkt_tx_ctrl;
ptx_pd->flags = flags;
ptx_pd->priority = WMM_HIGHEST_PRIORITY;
ptx_pd->tx_pkt_offset = sizeof(TxPD);
endian_convert_TxPD(ptx_pd);
ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, pmbuf, MNULL);
switch (ret) {
case MLAN_STATUS_RESOURCE:
pmadapter->data_sent = MTRUE;
/* Fall through FAILURE handling */
case MLAN_STATUS_FAILURE:
wlan_free_mlan_buffer(&pmadapter->callbacks, pmbuf);
PRINTM(MERROR, "TX Error: wlan_send_null_packet failed! ret=%d\n", ret);
pmadapter->dbg.num_tx_host_to_card_failure++;
goto done;
case MLAN_STATUS_SUCCESS:
wlan_free_mlan_buffer(&pmadapter->callbacks, pmbuf);
PRINTM(MDATA, "TX: wlan_send_null_packet succeeded!\n");
pmadapter->tx_lock_flag = MTRUE;
break;
case MLAN_STATUS_PENDING:
break;
default:
break;
}
pmadapter->callbacks.moal_get_system_time(&sec, &usec);
PRINTM(MDATA, "%lu.%lu : Null data => FW\n", sec, usec);
DBG_HEXDUMP(MDAT_D, "Null data", ptr, sizeof(TxPD) + INTF_HEADER_LEN);
done:
LEAVE();
return ret;
}
/**
* @brief This function checks if we need to send last packet indication.
*
* @param priv A pointer to mlan_private structure
*
* @return MTRUE or MFALSE
*/
t_u8
wlan_check_last_packet_indication(pmlan_private priv)
{
pmlan_adapter pmadapter = priv->adapter;
t_u8 ret = MFALSE;
t_u8 prop_ps = MTRUE;
ENTER();
if (!pmadapter->sleep_period.period) {
LEAVE();
return ret;
}
if (wlan_wmm_lists_empty(pmadapter)) {
if ((
/* TODO */
(priv->curr_bss_params.wmm_uapsd_enabled == MTRUE) &&
priv->wmm_qosinfo) || prop_ps)
ret = MTRUE;
}
if (ret && !pmadapter->cmd_sent && !pmadapter->curr_cmd
&& !IS_COMMAND_PENDING(pmadapter)) {
pmadapter->delay_null_pkt = MFALSE;
ret = MTRUE;
} else {
ret = MFALSE;
pmadapter->delay_null_pkt = MTRUE;
}
LEAVE();
return ret;
}