blob: 794e622324c12546b96699cfd534ce9a380a86df [file] [log] [blame]
/** @file mlan_11n_aggr.c
*
* @brief This file contains functions for 11n Aggregation.
*
* Copyright (C) 2008-2009, Marvell International Ltd.
* All Rights Reserved
*/
/********************************************************
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"
#include "mlan_sdio.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(amsdu_buf, data, (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(amsdu_buf + amsdu_buf_offset, &snap, LLC_SNAP_LEN);
amsdu_buf_offset += LLC_SNAP_LEN;
memcpy(amsdu_buf + amsdu_buf_offset, data + 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(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 = (t_u8) mbuf->bss_num;
if (pmadapter->ps_state != PS_STATE_AWAKE) {
if (MTRUE == wlan_check_last_packet_indication(priv)) {
pmadapter->tx_lock_flag = MTRUE;
ptx_pd->flags = MRVDRV_TxPD_POWER_MGMT_LAST_PACKET;
}
}
/* 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 pmadapter A pointer to mlan_adapter structure
* @param mbuf TxPD buffer
*
* @return N/A
*/
static INLINE void
wlan_11n_update_pktlen_amsdu_txpd(pmlan_adapter pmadapter, 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));
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;
while (total_pkt_len > 0) {
/* Length will be in network format, change it to host */
pkt_len = mlan_ntohs((*(t_u16 *) (data + (2 * MLAN_MAC_ADDR_LENGTH))));
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;
}
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;
int pad;
mlan_status ret = MLAN_STATUS_FAILURE;
RxPacketHdr_t *prx_pkt;
mlan_buffer *daggr_mbuf;
mlan_adapter *pmadapter = priv->adapter;
t_u8 rfc1042_eth_hdr[MLAN_MAC_ADDR_LENGTH] = { 0xaa, 0xaa, 0x03,
0x00, 0x00, 0x00
};
ENTER();
data = (t_u8 *) (pmbuf->pbuf + pmbuf->data_offset);
total_pkt_len = pmbuf->data_len;
/* Sanity test */
if (total_pkt_len > MLAN_RX_DATA_BUF_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);
while (total_pkt_len > 0) {
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 pkt_len %d %d\n", total_pkt_len, pkt_len);
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(&prx_pkt->rfc1042_hdr,
rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) {
memmove(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);
}
if ((pmadapter->callbacks.moal_malloc(sizeof(mlan_buffer),
(t_u8 **) & daggr_mbuf))) {
PRINTM(MERROR, "Error allocating daggr mlan_buffer\n");
return MLAN_STATUS_FAILURE;
}
memcpy(daggr_mbuf, pmbuf, sizeof(mlan_buffer));
daggr_mbuf->data_offset = 0;
daggr_mbuf->data_len = pkt_len;
daggr_mbuf->pbuf = data;
daggr_mbuf->pdesc = MNULL;
daggr_mbuf->pparent = pmbuf;
daggr_mbuf->priority = pmbuf->priority;
ret =
pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle,
daggr_mbuf);
switch (ret) {
case MLAN_STATUS_PENDING:
break;
case MLAN_STATUS_FAILURE:
PRINTM(MERROR, "Deaggr, send to moal failed\n");
case MLAN_STATUS_SUCCESS:
wlan_recv_packet_complete(pmadapter, daggr_mbuf, ret);
break;
default:
break;
}
data += pkt_len + pad;
}
done:
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
*/
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;
t_u32 sec, usec;
mlan_tx_param tx_param;
ENTER();
PRINTM(MDAT_D, "Handling Aggr packet\n");
if ((pmbuf_src = (pmlan_buffer) util_peek_list(&pra_list->buf_head,
pmadapter->callbacks.
moal_spin_lock,
pmadapter->callbacks.
moal_spin_unlock))) {
if (!
(pmbuf_aggr =
wlan_alloc_mlan_buffer(&pmadapter->callbacks,
pmadapter->tx_buf_size))) {
PRINTM(MERROR, "Error allocating mlan_buffer\n");
return MLAN_STATUS_FAILURE;
}
data = pmbuf_aggr->pbuf + headroom;
memcpy(pmbuf_aggr, pmbuf_src, sizeof(mlan_buffer));
pmbuf_aggr->pbuf = data;
pmbuf_aggr->pdesc = MNULL;
pmbuf_aggr->data_offset = 0;
/* Form AMSDU */
wlan_11n_form_amsdu_txpd(priv, pmbuf_aggr);
pkt_size = sizeof(TxPD);
} else {
goto exit;
}
while (pmbuf_src && ((pkt_size + (pmbuf_src->data_len + LLC_SNAP_LEN)
+ headroom)
<= pmadapter->tx_buf_size)) {
pmbuf_src = (pmlan_buffer)
util_dequeue_list(&pra_list->buf_head,
pmadapter->callbacks.moal_spin_lock,
pmadapter->callbacks.moal_spin_unlock);
pra_list->total_pkts_size -= pmbuf_src->data_len;
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
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));
pmadapter->callbacks.moal_send_packet_complete(pmadapter->pmoal_handle,
pmbuf_src,
MLAN_STATUS_SUCCESS);
pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock);
if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) {
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
LEAVE();
return MLAN_STATUS_FAILURE;
}
pmbuf_src = (pmlan_buffer) util_peek_list(&pra_list->buf_head,
pmadapter->callbacks.
moal_spin_lock,
pmadapter->callbacks.
moal_spin_unlock);
}
pmadapter->callbacks.moal_spin_unlock(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(pmadapter, pmbuf_aggr);
pmbuf_aggr->data_len += headroom;
pmbuf_aggr->pbuf = data - headroom;
tx_param.next_pkt_len = ((pra_list->total_pkts_size) ?
(((pra_list->total_pkts_size) >
pmadapter->tx_buf_size) ? pmadapter->
tx_buf_size : pra_list->total_pkts_size +
LLC_SNAP_LEN + sizeof(TxPD)) : 0);
ret =
wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, pmbuf_aggr,
&tx_param);
switch (ret) {
case MLAN_STATUS_RESOURCE:
pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock);
if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) {
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
wlan_write_data_complete(pmadapter, pmbuf_aggr,
MLAN_STATUS_FAILURE);
LEAVE();
return MLAN_STATUS_FAILURE;
}
util_enqueue_list_head(&pra_list->buf_head,
(pmlan_linked_list) pmbuf_aggr,
pmadapter->callbacks.moal_spin_lock,
pmadapter->callbacks.moal_spin_unlock);
pra_list->total_pkts_size += pmbuf_aggr->data_len;
pmbuf_aggr->flags = MLAN_BUF_FLAG_REQUEUED_PKT;
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
PRINTM(MDATA, "MLAN_STATUS_RESOURCE is returned\n");
break;
case MLAN_STATUS_FAILURE:
pmadapter->data_sent = MFALSE;
PRINTM(MERROR, "Error: moal_write_data_async failed: 0x%X\n", ret);
pmadapter->dbg.num_tx_host_to_card_failure++;
wlan_write_data_complete(pmadapter, pmbuf_aggr, ret);
goto exit;
case MLAN_STATUS_PENDING:
pmadapter->data_sent = MFALSE;
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(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(priv->wmm.ra_list_spinlock);
}
pmadapter->callbacks.moal_get_system_time(&sec, &usec);
PRINTM(MDATA, "%lu.%lu : Data => FW\n", sec, usec);
exit:
LEAVE();
return (pkt_size + headroom);
}