blob: 6b6dfe92fe6feedb02da8a8a05d1a8a391d5b038 [file] [log] [blame]
/** @file mlan_wmm.c
*
* @brief This file contains functions for WMM.
*
* Copyright (C) 2008-2009, Marvell International Ltd.
* All Rights Reserved
*/
/********************************************************
Change log:
10/24/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_sdio.h"
/********************************************************
Local Variables
********************************************************/
/** Maximum value FW can accept for driver delay in packet transmission */
#define DRV_PKT_DELAY_TO_FW_MAX 512
/*
* Upper and Lower threshold for packet queuing in the driver
* - When the number of packets queued reaches the upper limit,
* the driver will stop the net queue in the app/kernel space.
* - When the number of packets drops beneath the lower limit after
* having reached the upper limit, the driver will restart the net
* queue.
*/
/** Lower threshold for packet queuing in the driver.
* When the number of packets drops beneath the lower limit after having
* reached the upper limit, the driver will restart the net queue.
*/
#define WMM_QUEUED_PACKET_LOWER_LIMIT 180
/** Upper threshold for packet queuing in the driver.
* When the number of packets queued reaches the upper limit, the driver
* will stop the net queue in the app/kernel space.
*/
#define WMM_QUEUED_PACKET_UPPER_LIMIT 200
/** Offset for TOS field in the IP header */
#define IPTOS_OFFSET 5
/** WMM information IE */
static const t_u8 wmm_info_ie[] = { WMM_IE, 0x07,
0x00, 0x50, 0xf2, 0x02,
0x00, 0x01, 0x00
};
/**
* AC Priorities go from AC_BK to AC_VO. The ACI enumeration for AC_BK (1)
* is higher than the enumeration for AC_BE (0); hence the needed
* mapping conversion for wmm AC to priority Queue Index
*/
static const t_u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE,
WMM_AC_BK,
WMM_AC_VI,
WMM_AC_VO
};
/**
* This table will be used to store the tid values based on ACs.
* It is initialized to default values per TID.
*/
t_u8 tos_to_tid[] = {
/* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */
0x01, /* 0 1 0 AC_BK */
0x02, /* 0 0 0 AC_BK */
0x00, /* 0 0 1 AC_BE */
0x03, /* 0 1 1 AC_BE */
0x04, /* 1 0 0 AC_VI */
0x05, /* 1 0 1 AC_VI */
0x06, /* 1 1 0 AC_VO */
0x07 /* 1 1 1 AC_VO */
};
/**
* This table will provide the tid value for given ac. This table does not change
* and will be used to copy back the deafult values to tos_to_tid in case of
* disconnect.
*/
t_u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} };
/********************************************************
Local Functions
********************************************************/
#ifdef DEBUG_LEVEL2
/**
* @brief Debug print function to display the priority parameters for a WMM AC
*
* @param pac_param Pointer to the AC parameters to display
*
* @return N/A
*/
static void
wlan_wmm_ac_debug_print(const IEEEtypes_WmmAcParameters_t * pac_param)
{
const char *ac_str[] = { "BK", "BE", "VI", "VO" };
ENTER();
PRINTM(MINFO, "WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, "
"EcwMin=%d, EcwMax=%d, TxopLimit=%d\n",
ac_str[wmm_aci_to_qidx_map[pac_param->aci_aifsn.aci]],
pac_param->aci_aifsn.aci, pac_param->aci_aifsn.acm,
pac_param->aci_aifsn.aifsn, pac_param->ecw.ecw_min,
pac_param->ecw.ecw_max, wlan_le16_to_cpu(pac_param->tx_op_limit));
LEAVE();
}
/** Print the WMM AC for debug purpose */
#define PRINTM_AC(pac_param) wlan_wmm_ac_debug_print(pac_param)
#else
/** Print the WMM AC for debug purpose */
#define PRINTM_AC(pac_param)
#endif
/**
* @brief Allocate route address
*
* @param pmadapter Pointer to the mlan_adapter structure
* @param ra Pointer to the route address
*
* @return ra_list
*/
raListTbl *
wlan_wmm_allocate_ralist_node(pmlan_adapter pmadapter, t_u8 * ra)
{
raListTbl *ra_list;
ENTER();
if (pmadapter->callbacks.
moal_malloc(sizeof(raListTbl), (t_u8 **) & ra_list)) {
PRINTM(MERROR, "Fail to allocate ra_list\n");
goto done;
}
util_init_list((pmlan_linked_list) ra_list);
util_init_list_head(&ra_list->buf_head, MTRUE,
pmadapter->callbacks.moal_init_lock);
memcpy(ra_list->ra, ra, MLAN_MAC_ADDR_LENGTH);
ra_list->total_pkts_size = 0;
PRINTM(MINFO, "RAList: Allocating buffers for TID %p\n", ra_list);
done:
LEAVE();
return ra_list;
}
/**
* @brief This function cleans Tx/Rx queues
*
* @param priv A pointer to mlan_private
*
* @return N/A
*/
t_void
wlan_clean_txrx(pmlan_private priv)
{
mlan_adapter *pmadapter = priv->adapter;
ENTER();
pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock);
wlan_wmm_cleanup_queues(priv);
wlan_11n_cleanup_reorder_tbl(priv);
wlan_11n_deleteall_txbastream_tbl(priv);
#ifdef SDIO_MULTI_PORT_TX_AGGR
MP_TX_AGGR_BUF_RESET(priv->adapter);
#endif
#ifdef SDIO_MULTI_PORT_RX_AGGR
MP_RX_AGGR_BUF_RESET(priv->adapter);
#endif
wlan_wmm_delete_all_ralist(priv);
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid));
LEAVE();
}
/**
* @brief Allocate and add a RA list for all TIDs with the given RA
*
* @param priv Pointer to the mlan_private driver data struct
* @param ra Address of the receiver STA (AP in case of infra)
*
* @return N/A
*/
void
wlan_ralist_add(mlan_private * priv, t_u8 * ra)
{
int i;
raListTbl *ra_list;
pmlan_adapter pmadapter = priv->adapter;
ENTER();
for (i = 0; i < MAX_NUM_TID; ++i) {
ra_list = wlan_wmm_allocate_ralist_node(pmadapter, ra);
PRINTM(MINFO, "Creating RA List %p\n", ra_list);
if (!ra_list)
break;
if (queuing_ra_based(priv))
ra_list->is_11n_enabled = wlan_is_11n_enabled(priv, ra);
else
ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
PRINTM(MDATA, "ralist %p: is_11n_enabled=%d\n", ra_list,
ra_list->is_11n_enabled);
util_enqueue_list_tail(&priv->wmm.tid_tbl_ptr[i].ra_list,
(pmlan_linked_list) ra_list, MNULL, MNULL);
if (!priv->wmm.tid_tbl_ptr[i].ra_list_curr)
priv->wmm.tid_tbl_ptr[i].ra_list_curr = ra_list;
}
LEAVE();
}
/**
* @brief Set the WMM queue priorities to their default values
*
* @param priv Pointer to the mlan_private driver data struct
*
* @return N/A
*/
void
wlan_wmm_default_queue_priorities(pmlan_private priv)
{
ENTER();
/* Default queue priorities: VO->VI->BE->BK */
priv->wmm.queue_priority[0] = WMM_AC_VO;
priv->wmm.queue_priority[1] = WMM_AC_VI;
priv->wmm.queue_priority[2] = WMM_AC_BE;
priv->wmm.queue_priority[3] = WMM_AC_BK;
LEAVE();
}
/**
* @brief Map ACs to TID
*
* @param priv Pointer to the mlan_private driver data struct
* @param queue_priority Queue_priority structure
*
* @return N/A
*/
static void
wlan_wmm_queue_priorities_tid(pmlan_private priv, t_u8 queue_priority[])
{
int i;
ENTER();
for (i = 0; i < 4; ++i) {
tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1];
tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0];
}
LEAVE();
}
/**
* @brief Initialize WMM priority queues
*
* @param priv Pointer to the mlan_private driver data struct
* @param pwmm_ie Pointer to the IEEEtypes_WmmParameter_t data struct
*
* @return N/A
*/
void
wlan_wmm_setup_queue_priorities(pmlan_private priv,
IEEEtypes_WmmParameter_t * pwmm_ie)
{
t_u16 cw_min, avg_back_off, tmp[4];
t_u32 i, j, num_ac;
t_u8 ac_idx;
ENTER();
if (!pwmm_ie || priv->wmm_enabled == MFALSE) {
/* WMM is not enabled, just set the defaults and return */
wlan_wmm_default_queue_priorities(priv);
LEAVE();
return;
}
HEXDUMP("WMM: setup_queue_priorities: param IE",
(t_u8 *) pwmm_ie, sizeof(IEEEtypes_WmmParameter_t));
PRINTM(MINFO, "WMM Parameter IE: version=%d, "
"qos_info Parameter Set Count=%d, Reserved=%#x\n",
pwmm_ie->vend_hdr.version, pwmm_ie->qos_info.para_set_count,
pwmm_ie->reserved);
for (num_ac = 0; num_ac < NELEMENTS(pwmm_ie->ac_params); num_ac++) {
cw_min = (1 << pwmm_ie->ac_params[num_ac].ecw.ecw_min) - 1;
avg_back_off =
(cw_min >> 1) + pwmm_ie->ac_params[num_ac].aci_aifsn.aifsn;
ac_idx = wmm_aci_to_qidx_map[pwmm_ie->ac_params[num_ac].aci_aifsn.aci];
priv->wmm.queue_priority[ac_idx] = ac_idx;
tmp[ac_idx] = avg_back_off;
PRINTM(MINFO, "WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n",
(1 << pwmm_ie->ac_params[num_ac].ecw.ecw_max) - 1,
cw_min, avg_back_off);
PRINTM_AC(&pwmm_ie->ac_params[num_ac]);
}
HEXDUMP("WMM: avg_back_off", (t_u8 *) tmp, sizeof(tmp));
HEXDUMP("WMM: queue_priority", priv->wmm.queue_priority,
sizeof(priv->wmm.queue_priority));
/* Bubble sort */
for (i = 0; i < num_ac; i++) {
for (j = 1; j < num_ac - i; j++) {
if (tmp[j - 1] > tmp[j]) {
SWAP_U16(tmp[j - 1], tmp[j]);
SWAP_U8(priv->wmm.queue_priority[j - 1],
priv->wmm.queue_priority[j]);
} else if (tmp[j - 1] == tmp[j]) {
if (priv->wmm.queue_priority[j - 1]
< priv->wmm.queue_priority[j]) {
SWAP_U8(priv->wmm.queue_priority[j - 1],
priv->wmm.queue_priority[j]);
}
}
}
}
wlan_wmm_queue_priorities_tid(priv, priv->wmm.queue_priority);
HEXDUMP("WMM: avg_back_off, sort", (t_u8 *) tmp, sizeof(tmp));
HEXDUMP("WMM: queue_priority, sort", priv->wmm.queue_priority,
sizeof(priv->wmm.queue_priority));
LEAVE();
}
/**
* @brief Evaluate whether or not an AC is to be downgraded
*
* @param priv Pointer to the mlan_private driver data struct
* @param eval_ac AC to evaluate for downgrading
*
* @return WMM AC The eval_ac traffic is to be sent on.
*/
static mlan_wmm_ac_e
wlan_wmm_eval_downgrade_ac(pmlan_private priv, mlan_wmm_ac_e eval_ac)
{
int down_ac;
mlan_wmm_ac_e ret_ac;
WmmAcStatus_t *pac_status;
ENTER();
pac_status = &priv->wmm.ac_status[eval_ac];
if (pac_status->disabled == MFALSE) {
LEAVE();
/* Okay to use this AC, its enabled */
return eval_ac;
}
/* Setup a default return value of the lowest priority */
ret_ac = WMM_AC_BK;
/*
* Find the highest AC that is enabled and does not require admission
* control. The spec disallows downgrading to an AC, which is enabled
* due to a completed admission control. Unadmitted traffic is not
* to be sent on an AC with admitted traffic.
*/
for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) {
pac_status = &priv->wmm.ac_status[down_ac];
if ((pac_status->disabled == MFALSE)
&& (pac_status->flow_required == MFALSE))
/* AC is enabled and does not require admission control */
ret_ac = (mlan_wmm_ac_e) down_ac;
}
LEAVE();
return ret_ac;
}
/**
* @brief Downgrade WMM priority queue
*
* @param priv Pointer to the mlan_private driver data struct
*
* @return N/A
*/
void
wlan_wmm_setup_ac_downgrade(pmlan_private priv)
{
int ac_val;
ENTER();
PRINTM(MINFO, "WMM: AC Priorities: BK(0), BE(1), VI(2), VO(3)\n");
if (priv->wmm_enabled == MFALSE) {
/* WMM is not enabled, default priorities */
for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) {
for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) {
priv->wmm.ac_down_graded_vals[ac_val] = (mlan_wmm_ac_e) ac_val;
}
}
} else {
for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) {
priv->wmm.ac_down_graded_vals[ac_val]
= wlan_wmm_eval_downgrade_ac(priv, (mlan_wmm_ac_e) ac_val);
PRINTM(MINFO, "WMM: AC PRIO %d maps to %d\n",
ac_val, priv->wmm.ac_down_graded_vals[ac_val]);
}
}
LEAVE();
}
/**
* @brief Convert the IP TOS field to an WMM AC Queue assignment
*
* @param pmadapter A pointer to mlan_adapter structure
* @param tos IP TOS field
*
* @return WMM AC Queue mapping of the IP TOS field
*/
static mlan_wmm_ac_e
wlan_wmm_convert_tos_to_ac(pmlan_adapter pmadapter, t_u32 tos)
{
/* Map of TOS UP values to WMM AC */
const mlan_wmm_ac_e tos_to_ac[] = { WMM_AC_BE,
WMM_AC_BK,
WMM_AC_BK,
WMM_AC_BE,
WMM_AC_VI,
WMM_AC_VI,
WMM_AC_VO,
WMM_AC_VO
};
ENTER();
if (tos >= NELEMENTS(tos_to_ac)) {
LEAVE();
return WMM_AC_BE;
}
LEAVE();
return tos_to_ac[tos];
}
/**
* @brief Evaluate a given TID and downgrade it to a lower TID if the
* WMM Parameter IE received from the AP indicates that the AP
* is disabled (due to call admission control (ACM bit). Mapping
* of TID to AC is taken care internally
*
* @param priv Pointer to the mlan_private data struct
* @param tid tid to evaluate for downgrading
*
* @return Same tid as input if downgrading not required or
* the tid the traffic for the given tid should be downgraded to
*/
static t_u8 INLINE
wlan_wmm_downgrade_tid(pmlan_private priv, t_u32 tid)
{
mlan_wmm_ac_e ac_down;
pmlan_adapter pmadapter = priv->adapter;
ENTER();
ac_down =
priv->wmm.
ac_down_graded_vals[wlan_wmm_convert_tos_to_ac(pmadapter, tid)];
LEAVE();
/*
* Send the index to tid array, picking from the array will be
* taken care by dequeuing function
*/
return ac_to_tid[ac_down][tid % 2];
}
/********************************************************
Global Functions
********************************************************/
/**
* @brief Initialize the WMM state information and the WMM data path queues.
*
* @param pmadapter Pointer to the mlan_adapter data structure
*
* @return N/A
*/
t_void
wlan_wmm_init(pmlan_adapter pmadapter)
{
int i, j;
pmlan_private priv;
ENTER();
for (j = 0; j < MLAN_MAX_BSS_NUM; ++j) {
if ((priv = pmadapter->priv[j])) {
memset(&priv->wmm, 0x00, sizeof(priv->wmm));
for (i = 0; i < MAX_NUM_TID; ++i) {
priv->aggr_prio_tbl[i].amsdu = tos_to_tid[i];
priv->aggr_prio_tbl[i].ampdu_ap =
priv->aggr_prio_tbl[i].ampdu_user = tos_to_tid[i];
priv->wmm.tid_tbl_ptr[i].ra_list_curr = MNULL;
util_init_list_head(&priv->wmm.tid_tbl_ptr[i].ra_list, MTRUE,
priv->adapter->callbacks.moal_init_lock);
}
priv->aggr_prio_tbl[6].amsdu = priv->aggr_prio_tbl[6].ampdu_ap
= priv->aggr_prio_tbl[6].ampdu_user = BA_STREAM_NOT_ALLOWED;
priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap
= priv->aggr_prio_tbl[7].ampdu_user = BA_STREAM_NOT_ALLOWED;
priv->add_ba_param.timeout = MLAN_DEFAULT_BLOCK_ACK_TIMEOUT;
priv->add_ba_param.tx_win_size = MLAN_AMPDU_DEF_TXWINSIZE;
priv->add_ba_param.rx_win_size = MLAN_AMPDU_DEF_RXWINSIZE;
priv->adapter->callbacks.moal_init_lock(&priv->wmm.
ra_list_spinlock);
}
}
LEAVE();
}
/**
* @brief Setup the queue priorities and downgrade any queues as required
* by the WMM info. Setups default values if WMM is not active
* for this association.
*
* @param priv Pointer to the mlan_private driver data struct
*
* @return N/A
*/
void
wlan_wmm_setup_queues(pmlan_private priv)
{
ENTER();
wlan_wmm_setup_queue_priorities(priv, MNULL);
wlan_wmm_setup_ac_downgrade(priv);
LEAVE();
}
/**
* @brief Send a command to firmware to retrieve the current WMM status
*
* @param priv Pointer to the mlan_private driver data struct
*
* @return MLAN_STATUS_SUCCESS; MLAN_STATUS_FAILURE
*/
int
wlan_cmd_wmm_status_change(pmlan_private priv)
{
return wlan_prepare_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, 0, 0, 0, MNULL);
}
/**
* @brief Check if RA list is empty or not
*
* @param pmadapter A pointer to mlan_adapter structure
* @param ra_list_hhead RA list header
*
* @return MFALSE if not empty; MTRUE if empty
*/
static INLINE t_u8
wlan_wmm_is_ra_list_empty(pmlan_adapter pmadapter,
mlan_list_head * ra_list_hhead)
{
raListTbl *ra_list;
ra_list = (raListTbl *) ra_list_hhead->pnext;
while (ra_list != (raListTbl *) ra_list_hhead) {
if (util_peek_list(&ra_list->buf_head,
pmadapter->callbacks.moal_spin_lock,
pmadapter->callbacks.moal_spin_unlock)) {
LEAVE();
return MFALSE;
}
ra_list = (raListTbl *) ra_list->pnext;
}
return MTRUE;
}
/**
* @brief Check if wmm TX queue is empty
*
* @param pmadapter Pointer to the mlan_adapter driver data struct
*
* @return MFALSE if not empty; MTRUE if empty
*/
int
wlan_wmm_lists_empty(pmlan_adapter pmadapter)
{
int i, j;
pmlan_private priv;
ENTER();
for (j = 0; j < MLAN_MAX_BSS_NUM; ++j) {
if ((priv = pmadapter->priv[j])) {
for (i = 0; i < MAX_NUM_TID; i++) {
if (!wlan_wmm_is_ra_list_empty(pmadapter,
&priv->wmm.tid_tbl_ptr[i].
ra_list)) {
LEAVE();
return MFALSE;
}
}
}
}
LEAVE();
return MTRUE;
}
/**
* @brief Delete packets in RA node
*
* @param priv Pointer to the mlan_private driver data struct
* @param ra_list Pointer to raListTbl
*
* @return N/A
*/
static INLINE void
wlan_wmm_del_pkts_in_ralist_node(pmlan_private priv, raListTbl * ra_list)
{
pmlan_buffer pmbuf;
pmlan_adapter pmadapter = priv->adapter;
ENTER();
while ((pmbuf = (pmlan_buffer) util_peek_list(&ra_list->buf_head,
MNULL, MNULL))) {
util_unlink_list(&ra_list->buf_head, (pmlan_linked_list) pmbuf, MNULL,
MNULL);
pmadapter->callbacks.moal_send_packet_complete(pmadapter->pmoal_handle,
pmbuf,
MLAN_STATUS_FAILURE);
}
util_free_list_head(&ra_list->buf_head,
pmadapter->callbacks.moal_free_lock);
LEAVE();
}
/**
* @brief Delete packets in RA list
*
* @param priv Pointer to the mlan_private driver data struct
* @param ra_list_head ra list header
*
* @return N/A
*/
static INLINE void
wlan_wmm_del_pkts_in_ralist(pmlan_private priv, mlan_list_head * ra_list_head)
{
raListTbl *ra_list;
ENTER();
ra_list = (raListTbl *) util_peek_list(ra_list_head, MNULL, MNULL);
while (ra_list && ra_list != (raListTbl *) ra_list_head) {
wlan_wmm_del_pkts_in_ralist_node(priv, ra_list);
ra_list = ra_list->pnext;
}
LEAVE();
}
/**
* @brief Clean up the wmm queue
*
* @param priv Pointer to the mlan_private driver data struct
*
* @return N/A
*/
void
wlan_wmm_cleanup_queues(pmlan_private priv)
{
int i;
ENTER();
for (i = 0; i < MAX_NUM_TID; i++) {
wlan_wmm_del_pkts_in_ralist(priv, &priv->wmm.tid_tbl_ptr[i].ra_list);
}
LEAVE();
}
/**
* @brief Clean up initilized queues
*
* @param priv Pointer to the mlan_private driver data struct
*
* @return N/A
*/
void
wlan_wmm_cleanup_node(pmlan_private priv)
{
int i;
for (i = 0; i < MAX_NUM_TID; ++i)
util_free_list_head(&priv->wmm.tid_tbl_ptr[i].ra_list,
priv->adapter->callbacks.moal_free_lock);
util_free_list_head(&priv->tx_ba_stream_tbl_ptr,
priv->adapter->callbacks.moal_free_lock);
util_free_list_head(&priv->rx_reorder_tbl_ptr,
priv->adapter->callbacks.moal_free_lock);
}
/**
* @brief Delete all route address from RA list
*
* @param priv Pointer to the mlan_private driver data struct
*
* @return N/A
*/
void
wlan_wmm_delete_all_ralist(pmlan_private priv)
{
raListTbl *ra_list;
int i;
pmlan_adapter pmadapter = priv->adapter;
ENTER();
for (i = 0; i < MAX_NUM_TID; ++i) {
PRINTM(MINFO, "RAList: Freeing buffers for TID %d\n", i);
while ((ra_list =
(raListTbl *) util_peek_list(&priv->wmm.tid_tbl_ptr[i].ra_list,
MNULL, MNULL))) {
util_unlink_list(&priv->wmm.tid_tbl_ptr[i].ra_list,
(pmlan_linked_list) ra_list, MNULL, MNULL);
pmadapter->callbacks.moal_mfree((t_u8 *) ra_list);
}
util_init_list((pmlan_linked_list)
& priv->wmm.tid_tbl_ptr[i].ra_list);
priv->wmm.tid_tbl_ptr[i].ra_list_curr = MNULL;
}
LEAVE();
}
/**
* @brief Get ralist node
*
* @param priv Pointer to the mlan_private driver data struct
* @param tid TID
* @param ra_addr Pointer to the route address
*
* @return ra_list or MNULL
*/
raListTbl *
wlan_wmm_get_ralist_node(pmlan_private priv, t_u8 tid, t_u8 * ra_addr)
{
raListTbl *ra_list;
ENTER();
ra_list = (raListTbl *) util_peek_list(&priv->wmm.tid_tbl_ptr[tid].ra_list,
MNULL, MNULL);
while (ra_list && (ra_list != (raListTbl *)
& priv->wmm.tid_tbl_ptr[tid].ra_list)) {
if (!memcmp(ra_list->ra, ra_addr, MLAN_MAC_ADDR_LENGTH)) {
LEAVE();
return ra_list;
}
ra_list = ra_list->pnext;
}
LEAVE();
return MNULL;
}
/**
* @brief Get queue RA pointer
*
* @param priv Pointer to the mlan_private driver data struct
* @param tid TID
* @param ra_addr Pointer to the route address
*
* @return ra_list
*/
raListTbl *
wlan_wmm_get_queue_raptr(pmlan_private priv, t_u8 tid, t_u8 * ra_addr)
{
raListTbl *ra_list;
ENTER();
ra_list = wlan_wmm_get_ralist_node(priv, tid, ra_addr);
if (ra_list) {
LEAVE();
return ra_list;
}
wlan_ralist_add(priv, ra_addr);
LEAVE();
return wlan_wmm_get_ralist_node(priv, tid, ra_addr);
}
int
wlan_is_ralist_valid(mlan_private * priv, raListTbl * ra_list, int ptrindex)
{
raListTbl *rlist;
rlist = (raListTbl *) util_peek_list(&priv->wmm.
tid_tbl_ptr[ptrindex].ra_list, MNULL,
MNULL);
while (rlist && (rlist != (raListTbl *)
& priv->wmm.tid_tbl_ptr[ptrindex].ra_list)) {
if (rlist == ra_list)
return MTRUE;
rlist = rlist->pnext;
}
return MFALSE;
}
/**
* @brief Add packet to WMM queue
*
* @param pmadapter Pointer to the mlan_adapter driver data struct
* @param pmbuf Pointer to the mlan_buffer data struct
*
* @return N/A
*/
t_void
wlan_wmm_add_buf_txqueue(pmlan_adapter pmadapter, pmlan_buffer pmbuf)
{
pmlan_private priv = pmadapter->priv[pmbuf->bss_num];
t_u32 tid;
raListTbl *ra_list;
t_u8 ra[MLAN_MAC_ADDR_LENGTH], tid_down;
ENTER();
pmbuf->buf_type = MLAN_BUF_TYPE_DATA;
pmbuf->flags = 0;
tid = pmbuf->priority;
tid_down = wlan_wmm_downgrade_tid(priv, tid);
pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock);
/* In case of infra as we have already created the list during association
we just don't have to call get_queue_raptr, we will have only 1 raptr
for a tid in case of infra */
if (!queuing_ra_based(priv)) {
ra_list =
(raListTbl *) util_peek_list(&priv->wmm.tid_tbl_ptr[tid_down].
ra_list, MNULL, MNULL);
} else {
memcpy(ra, pmbuf->pbuf + pmbuf->data_offset, MLAN_MAC_ADDR_LENGTH);
ra_list = wlan_wmm_get_queue_raptr(priv, tid_down, ra);
}
if (!ra_list) {
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
LEAVE();
return;
}
PRINTM(MDAT_D, "Adding pkt to ra_list %p %p\n", ra_list, pmbuf);
util_enqueue_list_tail(&ra_list->buf_head,
(pmlan_linked_list) pmbuf, MNULL, MNULL);
ra_list->total_pkts_size += pmbuf->data_len;
// PRINTM(MERROR, "[+%d->%u] ", pmbuf->data_len, ra_list->total_pkts_size);
/* Record the current time the packet was queued; used to determine the
amount of time the packet was queued in the driver before it was sent to
the firmware. The delay is then sent along with the packet to the
firmware for aggregate delay calculation for stats and MSDU lifetime
expiry. */
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
pmadapter->callbacks.moal_get_system_time(&pmbuf->in_ts_sec,
&pmbuf->in_ts_usec);
LEAVE();
}
/**
* @brief Process the GET_WMM_STATUS command response from firmware
*
* The GET_WMM_STATUS command returns multiple TLVs for:
* - Each AC Queue status
* - Current WMM Parameter IE
*
* This function parses the TLVs and then calls further functions
* to process any changes in the queue prioritize or state.
*
* @param priv Pointer to the mlan_private driver data struct
* @param resp Pointer to the command response buffer including TLVs
* TLVs for each queue and the WMM Parameter IE.
*
* @return MLAN_STATUS_SUCCESS
*/
mlan_status
wlan_ret_wmm_get_status(pmlan_private priv, const HostCmd_DS_COMMAND * resp)
{
t_u8 *pcurrent = (t_u8 *) & resp->params.get_wmm_status;
t_u32 resp_len = resp->size;
int valid = MTRUE;
MrvlIEtypes_Data_t *pTlvHdr;
MrvlIEtypes_WmmQueueStatus_t *pTlvWmmQStatus;
IEEEtypes_WmmParameter_t *pWmmParamIe = MNULL;
WmmAcStatus_t *pac_status;
ENTER();
PRINTM(MINFO, "WMM: WMM_GET_STATUS cmdresp received: %d\n", resp_len);
HEXDUMP("CMD_RESP: WMM_GET_STATUS", pcurrent, resp_len);
while ((resp_len >= sizeof(pTlvHdr->header)) && valid) {
pTlvHdr = (MrvlIEtypes_Data_t *) pcurrent;
pTlvHdr->header.len = wlan_le16_to_cpu(pTlvHdr->header.len);
switch (wlan_le16_to_cpu(pTlvHdr->header.type)) {
case TLV_TYPE_WMMQSTATUS:
pTlvWmmQStatus = (MrvlIEtypes_WmmQueueStatus_t *) pTlvHdr;
PRINTM(MINFO, "CMD_RESP: WMM_GET_STATUS: QSTATUS TLV: %d, %d, %d\n",
pTlvWmmQStatus->queue_index,
pTlvWmmQStatus->flow_required, pTlvWmmQStatus->disabled);
pac_status = &priv->wmm.ac_status[pTlvWmmQStatus->queue_index];
pac_status->disabled = pTlvWmmQStatus->disabled;
pac_status->flow_required = pTlvWmmQStatus->flow_required;
pac_status->flow_created = pTlvWmmQStatus->flow_created;
break;
case WMM_IE:
/*
* Point the regular IEEE IE 2 bytes into the Marvell IE
* and setup the IEEE IE type and length byte fields
*/
HEXDUMP("WMM: WMM TLV:", (t_u8 *) pTlvHdr, pTlvHdr->header.len + 4);
pWmmParamIe = (IEEEtypes_WmmParameter_t *) (pcurrent + 2);
pWmmParamIe->vend_hdr.len = (t_u8) pTlvHdr->header.len;
pWmmParamIe->vend_hdr.element_id = WMM_IE;
PRINTM(MINFO, "CMD_RESP: WMM_GET_STATUS: WMM Parameter Set: %d\n",
pWmmParamIe->qos_info.para_set_count);
memcpy((t_u8 *) & priv->curr_bss_params.bss_descriptor.wmm_ie,
pWmmParamIe, pWmmParamIe->vend_hdr.len + 2);
break;
default:
valid = MFALSE;
break;
}
pcurrent += (pTlvHdr->header.len + sizeof(pTlvHdr->header));
resp_len -= (pTlvHdr->header.len + sizeof(pTlvHdr->header));
}
wlan_wmm_setup_queue_priorities(priv, pWmmParamIe);
wlan_wmm_setup_ac_downgrade(priv);
wlan_recv_event(priv, MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE, MNULL);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Call back from the command module to allow insertion of a WMM TLV
*
* If the BSS we are associating to supports WMM, add the required WMM
* Information IE to the association request command buffer in the form
* of a Marvell extended IEEE IE.
*
* @param priv Pointer to the mlan_private driver data struct
* @param ppAssocBuf Output parameter: Pointer to the TLV output buffer,
* modified on return to point after the appended WMM TLV
* @param pWmmIE Pointer to the WMM IE for the BSS we are joining
* @param pHTCap Pointer to the HT IE for the BSS we are joining
*
* @return Length of data appended to the association tlv buffer
*/
t_u32
wlan_wmm_process_association_req(pmlan_private priv,
t_u8 ** ppAssocBuf,
IEEEtypes_WmmParameter_t * pWmmIE,
IEEEtypes_HTCap_t * pHTCap)
{
MrvlIEtypes_WmmParamSet_t *pwmm_tlv;
t_u32 ret_len = 0;
ENTER();
/* Null checks */
if (!ppAssocBuf) {
LEAVE();
return 0;
}
if (!(*ppAssocBuf)) {
LEAVE();
return 0;
}
if (!pWmmIE) {
LEAVE();
return 0;
}
PRINTM(MINFO, "WMM: process assoc req: bss->wmmIe=0x%x\n",
pWmmIE->vend_hdr.element_id);
if ((priv->wmm_required
|| (pHTCap && (pHTCap->ieee_hdr.element_id == HT_CAPABILITY)
&& (priv->adapter->config_bands & BAND_GN
|| priv->adapter->config_bands & BAND_AN))
)
&& pWmmIE->vend_hdr.element_id == WMM_IE) {
pwmm_tlv = (MrvlIEtypes_WmmParamSet_t *) * ppAssocBuf;
pwmm_tlv->header.type = (t_u16) wmm_info_ie[0];
pwmm_tlv->header.type = wlan_cpu_to_le16(pwmm_tlv->header.type);
pwmm_tlv->header.len = (t_u16) wmm_info_ie[1];
memcpy(pwmm_tlv->wmm_ie, &wmm_info_ie[2], pwmm_tlv->header.len);
if (pWmmIE->qos_info.qos_uapsd)
memcpy((t_u8 *) (pwmm_tlv->wmm_ie + pwmm_tlv->header.len
- sizeof(priv->wmm_qosinfo)),
&priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo));
ret_len = sizeof(pwmm_tlv->header) + pwmm_tlv->header.len;
pwmm_tlv->header.len = wlan_cpu_to_le16(pwmm_tlv->header.len);
HEXDUMP("ASSOC_CMD: WMM IE", (t_u8 *) pwmm_tlv, ret_len);
*ppAssocBuf += ret_len;
}
LEAVE();
return ret_len;
}
/**
* @brief Compute the time delay in the driver queues for a given packet.
*
* When the packet is received at the OS/Driver interface, the current
* time is set in the packet structure. The difference between the present
* time and that received time is computed in this function and limited
* based on pre-compiled limits in the driver.
*
* @param priv Pointer to the mlan_private driver data struct
* @param pmbuf Pointer to the mlan_buffer which has been previously timestamped
*
* @return Time delay of the packet in 2ms units after having limit applied
*/
t_u8
wlan_wmm_compute_driver_packet_delay(pmlan_private priv,
const pmlan_buffer pmbuf)
{
t_u8 retVal = 0;
t_u32 out_ts_sec, out_ts_usec, queue_delay;
ENTER();
priv->adapter->callbacks.moal_get_system_time(&out_ts_sec, &out_ts_usec);
queue_delay = (out_ts_sec - pmbuf->in_ts_sec) * 1000;
queue_delay += (out_ts_usec - pmbuf->in_ts_usec) / 1000;
/*
* Queue delay is passed as a uint8 in units of 2ms (ms shifted
* by 1). Min value (other than 0) is therefore 2ms, max is 510ms.
*
* Pass max value if queue_delay is beyond the uint8 range
*/
retVal = (t_u8) (MIN(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1);
PRINTM(MDATA, "WMM: Pkt Delay: %d ms, %d ms sent to FW\n",
queue_delay, retVal);
LEAVE();
return retVal;
}
/**
* @brief This function gets the highest priority list pointer
*
* @param pmadapter A pointer to mlan_adapter
* @param priv A pointer to mlan_private
* @param tid A pointer to return tid
*
* @return raListTbl
*/
static raListTbl *
wlan_wmm_get_highest_priolist_ptr(pmlan_adapter pmadapter,
pmlan_private * priv, int *tid)
{
pmlan_private priv_tmp;
raListTbl *ptr, *head;
mlan_bssprio_node *bssprio_node, *bssprio_head;
tid_tbl_t *tid_ptr;
int i, j;
ENTER();
PRINTM(MDAT_D, "POP\n");
for (j = MLAN_MAX_BSS_NUM - 1; j >= 0; --j) {
if (!(util_peek_list(&pmadapter->bssprio_tbl[j].bssprio_head,
pmadapter->callbacks.moal_spin_lock,
pmadapter->callbacks.moal_spin_unlock)))
continue;
if (pmadapter->bssprio_tbl[j].bssprio_cur == (mlan_bssprio_node *)
& pmadapter->bssprio_tbl[j].bssprio_head)
bssprio_head = bssprio_node =
pmadapter->bssprio_tbl[j].bssprio_cur->pnext;
else
bssprio_head = bssprio_node = pmadapter->bssprio_tbl[j].bssprio_cur;
do {
priv_tmp = bssprio_node->priv;
for (i = HIGH_PRIO_TID; i >= LOW_PRIO_TID; --i) {
tid_ptr = &(priv_tmp)->wmm.tid_tbl_ptr[tos_to_tid[i]];
if (!util_peek_list(&tid_ptr->ra_list,
pmadapter->callbacks.moal_spin_lock,
pmadapter->callbacks.moal_spin_unlock))
continue;
/*
* Always choose the next ra we transmitted
* last time, this way we pick the ra's in
* round robin fashion.
*/
if (tid_ptr->ra_list_curr->pnext !=
(raListTbl *) & tid_ptr->ra_list)
head = ptr = tid_ptr->ra_list_curr->pnext;
else
head = ptr = tid_ptr->ra_list_curr;
do {
if (util_peek_list(&ptr->buf_head,
pmadapter->callbacks.moal_spin_lock,
pmadapter->callbacks.moal_spin_unlock)) {
*priv = priv_tmp;
*tid = tos_to_tid[i];
LEAVE();
return ptr;
}
if ((ptr = ptr->pnext) == (raListTbl *) & tid_ptr->ra_list)
ptr = ptr->pnext;
} while (ptr != head);
}
if ((bssprio_node = bssprio_node->pnext) == (mlan_bssprio_node *)
& pmadapter->bssprio_tbl[j].bssprio_head)
bssprio_node = bssprio_node->pnext;
} while (bssprio_node != bssprio_head);
}
LEAVE();
return MNULL;
}
/**
* @brief This function gets the number of packets in the Tx queue
*
* @param priv A pointer to mlan_private
* @param ptr A pointer to RA list table
* @param maxBufSize Maximum buffer size
*
* @return Packet count
*/
static int
wlan_num_pkts_in_txq(mlan_private * priv, raListTbl * ptr, int maxBufSize)
{
int count = 0, total_size = 0;
pmlan_buffer pmbuf;
ENTER();
for (pmbuf = (pmlan_buffer) ptr->buf_head.pnext;
pmbuf != (pmlan_buffer) (&ptr->buf_head); pmbuf = pmbuf->pnext) {
total_size += pmbuf->data_len;
if (total_size < maxBufSize)
++count;
else
break;
}
LEAVE();
return count;
}
/**
* @brief This function sends a single packet
*
* @param priv A pointer to mlan_private
* @param ptr A pointer to RA list table
*
* @return N/A
*/
static void INLINE
wlan_send_single_packet(pmlan_private priv, raListTbl * ptr, int ptrindex)
{
pmlan_buffer pmbuf;
pmlan_buffer pmbuf_next;
mlan_tx_param tx_param;
pmlan_adapter pmadapter = priv->adapter;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if ((pmbuf = (pmlan_buffer) util_dequeue_list(&ptr->buf_head,
pmadapter->callbacks.
moal_spin_lock,
pmadapter->callbacks.
moal_spin_unlock))) {
PRINTM(MDATA, "Dequeuing the packet %p %p\n", ptr, pmbuf);
ptr->total_pkts_size -= pmbuf->data_len;
pmbuf_next =
(pmlan_buffer) util_peek_list(&ptr->buf_head, MNULL, MNULL);
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
tx_param.next_pkt_len =
((pmbuf_next) ? pmbuf_next->data_len + sizeof(TxPD) : 0);
status = wlan_process_tx(priv, pmbuf, &tx_param);
if (status == MLAN_STATUS_RESOURCE) {
/** Queue the packet back at the head */
PRINTM(MDAT_D, "Queuing pkt back to raList %p %p\n", ptr, pmbuf);
pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock);
if (!wlan_is_ralist_valid(priv, ptr, ptrindex)) {
pmadapter->callbacks.moal_spin_unlock(priv->wmm.
ra_list_spinlock);
wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE);
LEAVE();
return;
}
util_enqueue_list_head(&ptr->buf_head,
(pmlan_linked_list) pmbuf,
pmadapter->callbacks.moal_spin_lock,
pmadapter->callbacks.moal_spin_unlock);
ptr->total_pkts_size += pmbuf->data_len;
pmbuf->flags = MLAN_BUF_FLAG_REQUEUED_PKT;
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
} else {
pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock);
if (!wlan_is_ralist_valid(priv, ptr, ptrindex)) {
priv->wmm.packets_out[ptrindex]++;
priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = ptr;
}
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);
}
} else {
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
PRINTM(MDATA, "Nothing to send\n");
}
LEAVE();
}
/**
* @brief This function checks if this mlan_buffer is already processed.
*
* @param priv A pointer to mlan_private
* @param ptr A pointer to RA list table
*
* @return MTRUE or MFALSE
*/
static int INLINE
wlan_is_ptr_processed(mlan_private * priv, raListTbl * ptr)
{
pmlan_buffer pmbuf;
if ((pmbuf = (pmlan_buffer) util_peek_list(&ptr->buf_head, MNULL, MNULL))
&& (pmbuf->flags == MLAN_BUF_FLAG_REQUEUED_PKT))
return MTRUE;
return MFALSE;
}
/**
* @brief This function sends a single packet
*
* @param priv A pointer to mlan_private
* @param ptr A pointer to RA list table
*
* @return N/A
*/
static void INLINE
wlan_send_processed_packet(pmlan_private priv, raListTbl * ptr, int ptrindex)
{
pmlan_buffer pmbuf_next;
mlan_tx_param tx_param;
pmlan_buffer pmbuf;
pmlan_adapter pmadapter = priv->adapter;
mlan_status ret = MLAN_STATUS_FAILURE;
if ((pmbuf = (pmlan_buffer) util_dequeue_list(&ptr->buf_head,
priv->adapter->callbacks.
moal_spin_lock,
priv->adapter->callbacks.
moal_spin_unlock))) {
pmbuf_next =
(pmlan_buffer) util_peek_list(&ptr->buf_head, MNULL, MNULL);
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
tx_param.next_pkt_len =
((pmbuf_next) ? pmbuf_next->data_len + sizeof(TxPD) : 0);
ret =
wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, pmbuf, &tx_param);
switch (ret) {
case MLAN_STATUS_RESOURCE:
PRINTM(MDATA, "MLAN_STATUS_RESOURCE is returned\n");
pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock);
if (!wlan_is_ralist_valid(priv, ptr, ptrindex)) {
pmadapter->callbacks.moal_spin_unlock(priv->wmm.
ra_list_spinlock);
wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE);
LEAVE();
return;
}
util_enqueue_list_head(&ptr->buf_head,
(pmlan_linked_list) pmbuf,
priv->adapter->callbacks.moal_spin_lock,
priv->adapter->callbacks.moal_spin_unlock);
pmbuf->flags = MLAN_BUF_FLAG_REQUEUED_PKT;
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
break;
case MLAN_STATUS_FAILURE:
pmadapter->data_sent = MFALSE;
PRINTM(MERROR, "Error: moal_write_data failed: 0x%X\n", ret);
pmadapter->dbg.num_tx_host_to_card_failure++;
wlan_write_data_complete(pmadapter, pmbuf, ret);
break;
case MLAN_STATUS_PENDING:
pmadapter->data_sent = MFALSE;
default:
break;
}
if (ret != MLAN_STATUS_RESOURCE) {
pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock);
if (wlan_is_ralist_valid(priv, ptr, ptrindex)) {
priv->wmm.packets_out[ptrindex]++;
priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = ptr;
}
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);
}
} else {
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
}
}
/**
* @brief This function dequeues a packet
*
* @param pmadapter A pointer to mlan_adapter
*
* @return MLAN_STATUS_SUCCESS;
*/
static int
wlan_dequeue_tx_packet(pmlan_adapter pmadapter)
{
raListTbl *ptr;
pmlan_private priv = MNULL;
int ptrindex = 0;
t_u8 ra[MLAN_MAC_ADDR_LENGTH];
int tid = 0;
t_u8 avail_ports = 0;
int i;
ENTER();
if (!(ptr = wlan_wmm_get_highest_priolist_ptr(pmadapter, &priv, &ptrindex))) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
tid = wlan_get_tid(priv->adapter, ptr);
for (i = 1; i < pmadapter->mp_end_port; i++) {
if ((pmadapter->mp_wr_bitmap >> i) & 1)
avail_ports++;
}
PRINTM(MDATA,
"mp_wr_bitmap=0x%04x avail_ports=%d tid=%d tx_eligibility[%d]=%d\n",
pmadapter->mp_wr_bitmap, avail_ports, tid, tid,
pmadapter->tx_eligibility[tid]);
if (avail_ports < pmadapter->tx_eligibility[tid]) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
pmadapter->callbacks.moal_spin_lock(priv->wmm.ra_list_spinlock);
if (!wlan_is_ralist_valid(priv, ptr, ptrindex)) {
pmadapter->callbacks.moal_spin_unlock(priv->wmm.ra_list_spinlock);
LEAVE();
return MLAN_STATUS_FAILURE;
}
/* Note:- Spinlock are unlocked in wlan_send_processed_packet ,
wlan_send_single_packet or wlan_11n_aggregate_pkt. The spinlock would
be required for some parts of both of function. But, the the bulk of
these function will execute w/o spinlock. Unlocking the spinlock
inside these function will help us avoid taking the spinlock again,
check to see if the ptr is still valid and then proceed. This is done
purely to increase execution time. */
/* Note:- Also, anybody adding code which does not get into
wlan_send_processed_packet, wlan_send_single_packet, or
wlan_11n_aggregate_pkt should make sure ra_list_spinlock is freed.
Otherwise there would be a lock up. */
if (wlan_is_ptr_processed(priv, ptr)) {
wlan_send_processed_packet(priv, ptr, ptrindex);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
if (!ptr->is_11n_enabled || wlan_is_bastream_setup(priv, ptr)
|| ((priv->sec_info.ewpa_enabled == MFALSE) &&
((priv->sec_info.wpa_enabled
|| priv->sec_info.wpa2_enabled) &&
priv->wpa_is_gtk_set == MFALSE))
) {
wlan_send_single_packet(priv, ptr, ptrindex);
} else {
if (wlan_is_ampdu_allowed(priv, ptr)) {
if (wlan_is_bastream_avail(priv)) {
tid = wlan_get_tid(priv->adapter, ptr);
wlan_11n_create_txbastream_tbl(priv,
ptr->ra, tid,
BA_STREAM_SETUP_INPROGRESS);
wlan_send_addba(priv, tid, ptr->ra);
} else if (wlan_find_stream_to_delete(priv, ptr, &tid, ra)) {
wlan_11n_create_txbastream_tbl(priv,
ptr->ra,
wlan_get_tid(priv->adapter, ptr),
BA_STREAM_SETUP_INPROGRESS);
wlan_send_delba(priv, tid, ra, 1);
}
}
/** Minimum number of AMSDU */
#define MIN_NUM_AMSDU 2
if (wlan_is_amsdu_allowed(priv, ptr) &&
(wlan_num_pkts_in_txq(priv, ptr,
pmadapter->tx_buf_size) >= MIN_NUM_AMSDU)) {
wlan_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN, ptrindex);
} else {
wlan_send_single_packet(priv, ptr, ptrindex);
}
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Transmit the highest priority packet awaiting in the WMM Queues
*
* @param pmadapter Pointer to the mlan_adapter driver data struct
*
* @return N/A
*/
void
wlan_wmm_process_tx(pmlan_adapter pmadapter)
{
ENTER();
do {
/* Check if busy */
if (pmadapter->data_sent || pmadapter->tx_lock_flag)
break;
if (wlan_dequeue_tx_packet(pmadapter))
break;
} while (MTRUE);
LEAVE();
return;
}
/**
* @brief This function prepares the command of ADDTS
*
* @param pmpriv A pointer to mlan_private structure
* @param cmd A pointer to HostCmd_DS_COMMAND structure
* @param pdata_buf A pointer to data buffer
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
wlan_cmd_wmm_addts_req(IN pmlan_private pmpriv,
OUT HostCmd_DS_COMMAND * cmd, IN t_void * pdata_buf)
{
mlan_ds_wmm_addts *paddts = (mlan_ds_wmm_addts *) pdata_buf;
HostCmd_DS_WMM_ADDTS_REQ *pcmd_addts = &cmd->params.add_ts;
ENTER();
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_ADDTS_REQ);
cmd->size = wlan_cpu_to_le16(sizeof(pcmd_addts->dialog_token)
+ sizeof(pcmd_addts->timeout_ms)
+ sizeof(pcmd_addts->command_result)
+ sizeof(pcmd_addts->ieee_status_code)
+ paddts->tspec_data_len + S_DS_GEN);
cmd->result = 0;
pcmd_addts->timeout_ms = wlan_cpu_to_le32(paddts->timeout);
pcmd_addts->dialog_token = paddts->dialog_tok;
memcpy(pcmd_addts->tspec_data, paddts->tspec_data, paddts->tspec_data_len);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the command response of ADDTS
*
* @param pmpriv A pointer to mlan_private structure
* @param resp A pointer to HostCmd_DS_COMMAND
* @param pioctl_buf A pointer to mlan_ioctl_req structure
*
* @return MLAN_STATUS_SUCCESS
*/
mlan_status
wlan_ret_wmm_addts_req(IN pmlan_private pmpriv,
const IN HostCmd_DS_COMMAND * resp,
OUT mlan_ioctl_req * pioctl_buf)
{
mlan_ds_wmm_cfg *pwmm = MNULL;
mlan_ds_wmm_addts *paddts = MNULL;
const HostCmd_DS_WMM_ADDTS_REQ *presp_addts = &resp->params.add_ts;
ENTER();
if (pioctl_buf) {
pwmm = (mlan_ds_wmm_cfg *) pioctl_buf->pbuf;
paddts = (mlan_ds_wmm_addts *) & pwmm->param.addts;
paddts->result = presp_addts->command_result;
paddts->dialog_tok = presp_addts->dialog_token;
paddts->status_code = (t_u32) presp_addts->ieee_status_code;
if (presp_addts->command_result == MLAN_CMD_RESULT_SUCCESS) {
/* The tspecData field is potentially variable in size due to extra
IEs that may have been in the ADDTS response action frame.
Calculate the data length from the firmware command response. */
paddts->tspec_data_len = (t_u8) (resp->size
-
sizeof(presp_addts->command_result)
- sizeof(presp_addts->timeout_ms)
- sizeof(presp_addts->dialog_token)
-
sizeof(presp_addts->
ieee_status_code)
- S_DS_GEN);
/* Copy the TSPEC data include any extra IEs after the TSPEC */
memcpy(paddts->tspec_data, presp_addts->tspec_data,
paddts->tspec_data_len);
} else {
paddts->tspec_data_len = 0;
}
PRINTM(MINFO, "TSPEC: ADDTS ret = %d,%d sz=%d\n",
paddts->result, paddts->status_code, paddts->tspec_data_len);
HEXDUMP("TSPEC: ADDTS data",
paddts->tspec_data, paddts->tspec_data_len);
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function prepares the command of DELTS
*
* @param pmpriv A pointer to mlan_private structure
* @param cmd A pointer to HostCmd_DS_COMMAND structure
* @param pdata_buf A pointer to data buffer
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
wlan_cmd_wmm_delts_req(IN pmlan_private pmpriv,
OUT HostCmd_DS_COMMAND * cmd, IN t_void * pdata_buf)
{
mlan_ds_wmm_delts *pdelts = (mlan_ds_wmm_delts *) pdata_buf;
HostCmd_DS_WMM_DELTS_REQ *pcmd_delts = &cmd->params.del_ts;
ENTER();
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_DELTS_REQ);
cmd->size = wlan_cpu_to_le16(sizeof(pcmd_delts->dialog_token)
+ sizeof(pcmd_delts->command_result)
+ sizeof(pcmd_delts->ieee_reason_code)
+ pdelts->tspec_data_len + S_DS_GEN);
cmd->result = 0;
pcmd_delts->ieee_reason_code = (t_u8) pdelts->status_code;
memcpy(pcmd_delts->tspec_data, pdelts->tspec_data, pdelts->tspec_data_len);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the command response of DELTS
*
* @param pmpriv A pointer to mlan_private structure
* @param resp A pointer to HostCmd_DS_COMMAND
* @param pioctl_buf A pointer to mlan_ioctl_req structure
*
* @return MLAN_STATUS_SUCCESS
*/
mlan_status
wlan_ret_wmm_delts_req(IN pmlan_private pmpriv,
const IN HostCmd_DS_COMMAND * resp,
OUT mlan_ioctl_req * pioctl_buf)
{
mlan_ds_wmm_cfg *pwmm = MNULL;
const HostCmd_DS_WMM_DELTS_REQ *presp_delts = &resp->params.del_ts;
ENTER();
if (pioctl_buf) {
pwmm = (mlan_ds_wmm_cfg *) pioctl_buf->pbuf;
pwmm->param.delts.result = presp_delts->command_result;
PRINTM(MINFO, "TSPEC: DELTS result = %d\n",
presp_delts->command_result);
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function prepares the command of WMM_QUEUE_CONFIG
*
* @param pmpriv A pointer to mlan_private structure
* @param cmd A pointer to HostCmd_DS_COMMAND structure
* @param pdata_buf A pointer to data buffer
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
wlan_cmd_wmm_queue_config(IN pmlan_private pmpriv,
OUT HostCmd_DS_COMMAND * cmd, IN t_void * pdata_buf)
{
mlan_ds_wmm_queue_config *pqcfg = (mlan_ds_wmm_queue_config *) pdata_buf;
HostCmd_DS_WMM_QUEUE_CONFIG *pcmd_qcfg = &cmd->params.queue_config;
ENTER();
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_QUEUE_CONFIG);
cmd->size = wlan_cpu_to_le16(sizeof(pcmd_qcfg->action)
+ sizeof(pcmd_qcfg->access_category)
+ sizeof(pcmd_qcfg->msdu_lifetime_expiry)
+ S_DS_GEN);
cmd->result = 0;
pcmd_qcfg->action = pqcfg->action;
pcmd_qcfg->access_category = pqcfg->access_category;
pcmd_qcfg->msdu_lifetime_expiry =
wlan_cpu_to_le16(pqcfg->msdu_lifetime_expiry);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the command response of WMM_QUEUE_CONFIG
*
* @param pmpriv A pointer to mlan_private structure
* @param resp A pointer to HostCmd_DS_COMMAND
* @param pioctl_buf A pointer to mlan_ioctl_req structure
*
* @return MLAN_STATUS_SUCCESS
*/
mlan_status
wlan_ret_wmm_queue_config(IN pmlan_private pmpriv,
const IN HostCmd_DS_COMMAND * resp,
OUT mlan_ioctl_req * pioctl_buf)
{
mlan_ds_wmm_cfg *pwmm = MNULL;
const HostCmd_DS_WMM_QUEUE_CONFIG *presp_qcfg = &resp->params.queue_config;
ENTER();
if (pioctl_buf) {
pwmm = (mlan_ds_wmm_cfg *) pioctl_buf->pbuf;
pwmm->param.q_cfg.action = presp_qcfg->action;
pwmm->param.q_cfg.access_category = presp_qcfg->access_category;
pwmm->param.q_cfg.msdu_lifetime_expiry =
wlan_le16_to_cpu(presp_qcfg->msdu_lifetime_expiry);
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function prepares the command of WMM_QUEUE_STATS
*
* @param pmpriv A pointer to mlan_private structure
* @param cmd A pointer to HostCmd_DS_COMMAND structure
* @param pdata_buf A pointer to data buffer
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
wlan_cmd_wmm_queue_stats(IN pmlan_private pmpriv,
OUT HostCmd_DS_COMMAND * cmd, IN t_void * pdata_buf)
{
mlan_ds_wmm_queue_stats *pqstats = (mlan_ds_wmm_queue_stats *) pdata_buf;
HostCmd_DS_WMM_QUEUE_STATS *pcmd_qstats = &cmd->params.queue_stats;
t_u8 id;
ENTER();
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_QUEUE_STATS);
cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_WMM_QUEUE_STATS)
+ S_DS_GEN);
cmd->result = 0;
pcmd_qstats->action = pqstats->action;
pcmd_qstats->access_category = pqstats->access_category;
pcmd_qstats->pkt_count = wlan_cpu_to_le16(pqstats->pkt_count);
pcmd_qstats->pkt_loss = wlan_cpu_to_le16(pqstats->pkt_loss);
pcmd_qstats->avg_queue_delay = wlan_cpu_to_le32(pqstats->avg_queue_delay);
pcmd_qstats->avg_tx_delay = wlan_cpu_to_le32(pqstats->avg_tx_delay);
pcmd_qstats->used_time = wlan_cpu_to_le16(pqstats->used_time);
pcmd_qstats->policed_time = wlan_cpu_to_le16(pqstats->policed_time);
for (id = 0; id < MLAN_WMM_STATS_PKTS_HIST_BINS; id++) {
pcmd_qstats->delay_histogram[id] =
wlan_cpu_to_le16(pqstats->delay_histogram[id]);
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the command response of WMM_QUEUE_STATS
*
* @param pmpriv A pointer to mlan_private structure
* @param resp A pointer to HostCmd_DS_COMMAND
* @param pioctl_buf A pointer to mlan_ioctl_req structure
*
* @return MLAN_STATUS_SUCCESS
*/
mlan_status
wlan_ret_wmm_queue_stats(IN pmlan_private pmpriv,
const IN HostCmd_DS_COMMAND * resp,
OUT mlan_ioctl_req * pioctl_buf)
{
mlan_ds_wmm_cfg *pwmm = MNULL;
mlan_ds_wmm_queue_stats *pqstats = MNULL;
const HostCmd_DS_WMM_QUEUE_STATS *presp_qstats = &resp->params.queue_stats;
t_u8 id;
ENTER();
if (pioctl_buf) {
pwmm = (mlan_ds_wmm_cfg *) pioctl_buf->pbuf;
pqstats = (mlan_ds_wmm_queue_stats *) & pwmm->param.q_stats;
pqstats->action = presp_qstats->action;
pqstats->access_category = presp_qstats->access_category;
pqstats->pkt_count = wlan_le16_to_cpu(presp_qstats->pkt_count);
pqstats->pkt_loss = wlan_le16_to_cpu(presp_qstats->pkt_loss);
pqstats->avg_queue_delay =
wlan_le32_to_cpu(presp_qstats->avg_queue_delay);
pqstats->avg_tx_delay = wlan_le32_to_cpu(presp_qstats->avg_tx_delay);
pqstats->used_time = wlan_le16_to_cpu(presp_qstats->used_time);
pqstats->policed_time = wlan_le16_to_cpu(presp_qstats->policed_time);
for (id = 0; id < MLAN_WMM_STATS_PKTS_HIST_BINS; id++) {
pqstats->delay_histogram[id] =
wlan_le16_to_cpu(presp_qstats->delay_histogram[id]);
}
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function prepares the command of WMM_TS_STATUS
*
* @param pmpriv A pointer to mlan_private structure
* @param cmd A pointer to HostCmd_DS_COMMAND structure
* @param pdata_buf A pointer to data buffer
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
wlan_cmd_wmm_ts_status(IN pmlan_private pmpriv,
OUT HostCmd_DS_COMMAND * cmd, IN t_void * pdata_buf)
{
mlan_ds_wmm_ts_status *pts_status = (mlan_ds_wmm_ts_status *) pdata_buf;
HostCmd_DS_WMM_TS_STATUS *pcmd_ts_status = &cmd->params.ts_status;
ENTER();
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_TS_STATUS);
cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_WMM_TS_STATUS)
+ S_DS_GEN);
cmd->result = 0;
memcpy((t_void *) pcmd_ts_status, (t_void *) pts_status,
sizeof(HostCmd_DS_WMM_TS_STATUS));
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the command response of WMM_TS_STATUS
*
* @param pmpriv A pointer to mlan_private structure
* @param resp A pointer to HostCmd_DS_COMMAND
* @param pioctl_buf A pointer to mlan_ioctl_req structure
*
* @return MLAN_STATUS_SUCCESS
*/
mlan_status
wlan_ret_wmm_ts_status(IN pmlan_private pmpriv,
IN HostCmd_DS_COMMAND * resp,
OUT mlan_ioctl_req * pioctl_buf)
{
mlan_ds_wmm_cfg *pwmm = MNULL;
HostCmd_DS_WMM_TS_STATUS *presp_ts_status = &resp->params.ts_status;
ENTER();
if (pioctl_buf) {
pwmm = (mlan_ds_wmm_cfg *) pioctl_buf->pbuf;
presp_ts_status->medium_time =
wlan_le16_to_cpu(presp_ts_status->medium_time);
memcpy((t_void *) & pwmm->param.ts_status, (t_void *) presp_ts_status,
sizeof(mlan_ds_wmm_ts_status));
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}