mirror of
https://github.com/nxp-imx/mwifiex.git
synced 2024-11-01 05:25:21 +00:00
19732f578f
Corresponding firmware version: SDIO-UART W8987 Firmware version 16.92.21.p99 PCIE-UART W8997 Firmware version 16.92.21.p84.4 SDIO-UART W8997 Firmware version 16.92.21.p84.4 SDIO-UART IW416 Firmware version 16.92.21.p84.3 SDIO_UART IW612 Firmware version 18.99.2.p19.6 SDIO-UART W8801 Firmware version 14.92.36.p182 SDIO-UART W9098 Firmware version 17.92.1.p136.128 PCIE-UART W9098 Firmware version 17.92.1.p136.129 Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
3723 lines
103 KiB
C
3723 lines
103 KiB
C
/** @file mlan_wmm.c
|
|
*
|
|
* @brief This file contains functions for WMM.
|
|
*
|
|
*
|
|
* 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:
|
|
10/24/2008: initial version
|
|
********************************************************/
|
|
|
|
#include "mlan.h"
|
|
#ifdef STA_SUPPORT
|
|
#include "mlan_join.h"
|
|
#endif
|
|
#include "mlan_util.h"
|
|
#include "mlan_fw.h"
|
|
#include "mlan_main.h"
|
|
#include "mlan_wmm.h"
|
|
#include "mlan_11n.h"
|
|
#include "mlan_11ax.h"
|
|
#ifdef SDIO
|
|
#include "mlan_sdio.h"
|
|
#endif /* SDIO */
|
|
#ifdef PCIE
|
|
#include "mlan_pcie.h"
|
|
#endif /* PCIE */
|
|
|
|
/********************************************************
|
|
Local Variables
|
|
********************************************************/
|
|
|
|
/** WMM information IE */
|
|
static const t_u8 wmm_info_ie[] = {WMM_IE, 0x07, 0x00, 0x50, 0xf2,
|
|
0x02, 0x00, 0x01, 0x00};
|
|
|
|
/** Type enumeration of WMM AC_QUEUES */
|
|
typedef MLAN_PACK_START enum _wmm_ac_e {
|
|
AC_BE,
|
|
AC_BK,
|
|
AC_VI,
|
|
AC_VO
|
|
} MLAN_PACK_END wmm_ac_e;
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
static 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 inverses the tos_to_tid operation to get a priority
|
|
* which is in sequential order, and can be compared.
|
|
* Use this to compare the priority of two different TIDs.
|
|
*/
|
|
t_u8 tos_to_tid_inv[] = {0x02, /* from tos_to_tid[2] = 0 */
|
|
0x00, /* from tos_to_tid[0] = 1 */
|
|
0x01, /* from tos_to_tid[1] = 2 */
|
|
0x03, 0x04, 0x05, 0x06, 0x07};
|
|
|
|
/**
|
|
* This table will provide the tid value for given ac. This table does not
|
|
* change and will be used to copy back the default values to tos_to_tid in
|
|
* case of disconnect.
|
|
*/
|
|
t_u8 ac_to_tid[4][2] = {{1, 2}, {0, 3}, {4, 5}, {6, 7}};
|
|
|
|
/* Map of TOS UP values to WMM AC */
|
|
static 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};
|
|
|
|
raListTbl *wlan_wmm_get_ralist_node(pmlan_private priv, t_u8 tid,
|
|
t_u8 *ra_addr);
|
|
|
|
/********************************************************
|
|
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
|
|
*/
|
|
static raListTbl *wlan_wmm_allocate_ralist_node(pmlan_adapter pmadapter,
|
|
t_u8 *ra)
|
|
{
|
|
raListTbl *ra_list = MNULL;
|
|
|
|
ENTER();
|
|
|
|
if (pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle,
|
|
sizeof(raListTbl), MLAN_MEM_DEF,
|
|
(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((t_void *)pmadapter->pmoal_handle,
|
|
&ra_list->buf_head, MFALSE,
|
|
pmadapter->callbacks.moal_init_lock);
|
|
|
|
memcpy_ext(pmadapter, ra_list->ra, ra, MLAN_MAC_ADDR_LENGTH,
|
|
MLAN_MAC_ADDR_LENGTH);
|
|
|
|
ra_list->del_ba_count = 0;
|
|
ra_list->total_pkts = 0;
|
|
ra_list->tx_pause = 0;
|
|
PRINTM(MINFO, "RAList: Allocating buffers for TID %p\n", ra_list);
|
|
done:
|
|
LEAVE();
|
|
return ra_list;
|
|
}
|
|
|
|
/**
|
|
* @brief Add packet to TDLS pending TX queue
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param pmbuf Pointer to the mlan_buffer data struct
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static t_void wlan_add_buf_tdls_txqueue(pmlan_private priv, pmlan_buffer pmbuf)
|
|
{
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
ENTER();
|
|
util_enqueue_list_tail(pmadapter->pmoal_handle, &priv->tdls_pending_txq,
|
|
(pmlan_linked_list)pmbuf,
|
|
pmadapter->callbacks.moal_spin_lock,
|
|
pmadapter->callbacks.moal_spin_unlock);
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief Clean up the tdls pending TX queue
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static t_void wlan_cleanup_tdls_txq(pmlan_private priv)
|
|
{
|
|
pmlan_buffer pmbuf;
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
ENTER();
|
|
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->tdls_pending_txq.plock);
|
|
while ((pmbuf = (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle,
|
|
&priv->tdls_pending_txq,
|
|
MNULL, MNULL))) {
|
|
util_unlink_list(pmadapter->pmoal_handle,
|
|
&priv->tdls_pending_txq,
|
|
(pmlan_linked_list)pmbuf, MNULL, MNULL);
|
|
wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE);
|
|
}
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->tdls_pending_txq.plock);
|
|
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];
|
|
}
|
|
|
|
for (i = 0; i < MAX_NUM_TID; i++)
|
|
tos_to_tid_inv[tos_to_tid[i]] = (t_u8)i;
|
|
|
|
/* in case priorities have changed, force highest priority so
|
|
* next packet will check from top to re-establish the highest
|
|
*/
|
|
util_scalar_write(priv->adapter->pmoal_handle,
|
|
&priv->wmm.highest_queued_prio, HIGH_PRIO_TID,
|
|
priv->adapter->callbacks.moal_spin_lock,
|
|
priv->adapter->callbacks.moal_spin_unlock);
|
|
|
|
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 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
|
|
*/
|
|
mlan_wmm_ac_e wlan_wmm_convert_tos_to_ac(pmlan_adapter pmadapter, t_u32 tos)
|
|
{
|
|
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 INLINE t_u8 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
|
|
*/
|
|
if (tid == 1 || tid == 2)
|
|
return ac_to_tid[ac_down][(tid + 1) % 2];
|
|
else if (tid >= MAX_NUM_TID)
|
|
return ac_to_tid[ac_down][0];
|
|
else
|
|
return ac_to_tid[ac_down][tid % 2];
|
|
}
|
|
|
|
/**
|
|
* @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(pmadapter->pmoal_handle,
|
|
&ra_list->buf_head, MNULL,
|
|
MNULL))) {
|
|
util_unlink_list(pmadapter->pmoal_handle, &ra_list->buf_head,
|
|
(pmlan_linked_list)pmbuf, MNULL, MNULL);
|
|
wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE);
|
|
}
|
|
util_free_list_head((t_void *)pmadapter->pmoal_handle,
|
|
&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(priv->adapter->pmoal_handle,
|
|
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
|
|
*/
|
|
static 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);
|
|
priv->wmm.pkts_queued[i] = 0;
|
|
priv->wmm.pkts_paused[i] = 0;
|
|
}
|
|
util_scalar_write(priv->adapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued, 0, MNULL, MNULL);
|
|
util_scalar_write(priv->adapter->pmoal_handle,
|
|
&priv->wmm.highest_queued_prio, HIGH_PRIO_TID, MNULL,
|
|
MNULL);
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief Delete all route address from RA list
|
|
*
|
|
* @param priv Pointer to the mlan_private driver data struct
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static 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(
|
|
pmadapter->pmoal_handle,
|
|
&priv->wmm.tid_tbl_ptr[i].ra_list, MNULL,
|
|
MNULL))) {
|
|
util_unlink_list(pmadapter->pmoal_handle,
|
|
&priv->wmm.tid_tbl_ptr[i].ra_list,
|
|
(pmlan_linked_list)ra_list, MNULL,
|
|
MNULL);
|
|
|
|
pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle,
|
|
(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 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
|
|
*/
|
|
static raListTbl *wlan_wmm_get_queue_raptr(pmlan_private priv, t_u8 tid,
|
|
t_u8 *ra_addr)
|
|
{
|
|
raListTbl *ra_list;
|
|
#ifdef UAP_SUPPORT
|
|
t_u8 bcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
#endif
|
|
|
|
ENTER();
|
|
ra_list = wlan_wmm_get_ralist_node(priv, tid, ra_addr);
|
|
if (ra_list) {
|
|
LEAVE();
|
|
return ra_list;
|
|
}
|
|
#ifdef UAP_SUPPORT
|
|
if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) &&
|
|
(0 !=
|
|
memcmp(priv->adapter, ra_addr, bcast_addr, sizeof(bcast_addr)))) {
|
|
if (MNULL == wlan_get_station_entry(priv, ra_addr)) {
|
|
PRINTM_NETINTF(MERROR, priv);
|
|
PRINTM(MERROR,
|
|
"Drop packets to unknow station " MACSTR "\n",
|
|
MAC2STR(ra_addr));
|
|
LEAVE();
|
|
return MNULL;
|
|
}
|
|
}
|
|
#endif
|
|
wlan_ralist_add(priv, ra_addr);
|
|
|
|
ra_list = wlan_wmm_get_ralist_node(priv, tid, ra_addr);
|
|
LEAVE();
|
|
return ra_list;
|
|
}
|
|
|
|
#ifdef STA_SUPPORT
|
|
/**
|
|
* @brief Sends wmmac host event
|
|
*
|
|
* @param priv Pointer to the mlan_private driver data struct
|
|
* @param type_str Type of host event
|
|
* @param src_addr Pointer to the source Address
|
|
* @param tid TID
|
|
* @param up User priority
|
|
* @param status Status code or Reason code
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void wlan_send_wmmac_host_event(pmlan_private priv, char *type_str,
|
|
t_u8 *src_addr, t_u8 tid, t_u8 up,
|
|
t_u8 status)
|
|
{
|
|
t_u8 event_buf[100];
|
|
mlan_event *pevent;
|
|
t_u8 *pout_buf;
|
|
|
|
ENTER();
|
|
|
|
/* Format one of the following two output strings:
|
|
** - TSPEC:ADDTS_RSP:[<status code>]:TID=X:UP=Y
|
|
** - TSPEC:DELTS_RX:[<reason code>]:TID=X:UP=Y
|
|
*/
|
|
pevent = (mlan_event *)event_buf;
|
|
pout_buf = pevent->event_buf;
|
|
|
|
memcpy_ext(priv->adapter, pout_buf, (t_u8 *)"TSPEC:", 6, 6);
|
|
pout_buf += 6;
|
|
|
|
memcpy_ext(priv->adapter, pout_buf, (t_u8 *)type_str,
|
|
wlan_strlen(type_str), wlan_strlen(type_str));
|
|
pout_buf += wlan_strlen(type_str);
|
|
|
|
*pout_buf++ = ':';
|
|
*pout_buf++ = '[';
|
|
|
|
if (status >= 100) {
|
|
*pout_buf++ = (status / 100) + '0';
|
|
status = (status % 100);
|
|
}
|
|
|
|
if (status >= 10) {
|
|
*pout_buf++ = (status / 10) + '0';
|
|
status = (status % 10);
|
|
}
|
|
|
|
*pout_buf++ = status + '0';
|
|
|
|
memcpy_ext(priv->adapter, pout_buf, (t_u8 *)"]:TID", 5, 5);
|
|
pout_buf += 5;
|
|
*pout_buf++ = tid + '0';
|
|
|
|
memcpy_ext(priv->adapter, pout_buf, (t_u8 *)":UP", 3, 3);
|
|
pout_buf += 3;
|
|
*pout_buf++ = up + '0';
|
|
|
|
*pout_buf = '\0';
|
|
|
|
pevent->bss_index = priv->bss_index;
|
|
pevent->event_id = MLAN_EVENT_ID_DRV_REPORT_STRING;
|
|
pevent->event_len = wlan_strlen((const char *)(pevent->event_buf));
|
|
|
|
wlan_recv_event(priv, MLAN_EVENT_ID_DRV_REPORT_STRING, pevent);
|
|
LEAVE();
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
|
|
/**
|
|
* @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;
|
|
int next_prio = 0;
|
|
int next_tid = 0;
|
|
ENTER();
|
|
|
|
PRINTM(MDAT_D, "POP\n");
|
|
for (j = pmadapter->priv_num - 1; j >= 0; --j) {
|
|
if (!(util_peek_list(pmadapter->pmoal_handle,
|
|
&pmadapter->bssprio_tbl[j].bssprio_head,
|
|
MNULL, MNULL)))
|
|
continue;
|
|
|
|
if (pmadapter->bssprio_tbl[j].bssprio_cur ==
|
|
(mlan_bssprio_node *)&pmadapter->bssprio_tbl[j]
|
|
.bssprio_head) {
|
|
pmadapter->bssprio_tbl[j].bssprio_cur =
|
|
pmadapter->bssprio_tbl[j].bssprio_cur->pnext;
|
|
}
|
|
|
|
bssprio_head = bssprio_node =
|
|
pmadapter->bssprio_tbl[j].bssprio_cur;
|
|
|
|
do {
|
|
priv_tmp = bssprio_node->priv;
|
|
if ((priv_tmp->port_ctrl_mode == MTRUE) &&
|
|
(priv_tmp->port_open == MFALSE)) {
|
|
PRINTM(MINFO,
|
|
"get_highest_prio_ptr(): "
|
|
"PORT_CLOSED Ignore pkts from BSS%d\n",
|
|
priv_tmp->bss_index);
|
|
/* Ignore data pkts from a BSS if port is closed
|
|
*/
|
|
goto next_intf;
|
|
}
|
|
if (priv_tmp->tx_pause == MTRUE) {
|
|
PRINTM(MINFO,
|
|
"get_highest_prio_ptr(): "
|
|
"TX PASUE Ignore pkts from BSS%d\n",
|
|
priv_tmp->bss_index);
|
|
/* Ignore data pkts from a BSS if tx pause */
|
|
goto next_intf;
|
|
}
|
|
#if defined(USB)
|
|
if (!wlan_is_port_ready(pmadapter,
|
|
priv_tmp->port_index)) {
|
|
PRINTM(MINFO,
|
|
"get_highest_prio_ptr(): "
|
|
"usb port is busy,Ignore pkts from BSS%d\n",
|
|
priv_tmp->bss_index);
|
|
/* Ignore data pkts from a BSS if usb port is
|
|
* busy */
|
|
goto next_intf;
|
|
}
|
|
#endif
|
|
|
|
pmadapter->callbacks.moal_spin_lock(
|
|
pmadapter->pmoal_handle,
|
|
priv_tmp->wmm.ra_list_spinlock);
|
|
|
|
for (i = util_scalar_read(
|
|
pmadapter->pmoal_handle,
|
|
&priv_tmp->wmm.highest_queued_prio, MNULL,
|
|
MNULL);
|
|
i >= LOW_PRIO_TID; --i) {
|
|
tid_ptr = &(priv_tmp)
|
|
->wmm
|
|
.tid_tbl_ptr[tos_to_tid[i]];
|
|
if (!util_peek_list(pmadapter->pmoal_handle,
|
|
&tid_ptr->ra_list, MNULL,
|
|
MNULL))
|
|
continue;
|
|
|
|
/*
|
|
* Always choose the next ra we transmitted
|
|
* last time, this way we pick the ra's in
|
|
* round robin fashion.
|
|
*/
|
|
head = ptr = tid_ptr->ra_list_curr->pnext;
|
|
if (ptr == (raListTbl *)&tid_ptr->ra_list)
|
|
head = ptr = ptr->pnext;
|
|
|
|
do {
|
|
if (!ptr->tx_pause &&
|
|
util_peek_list(
|
|
pmadapter->pmoal_handle,
|
|
&ptr->buf_head, MNULL,
|
|
MNULL)) {
|
|
/* Because WMM only support
|
|
* BK/BE/VI/VO, we have 8 tid
|
|
* We should balance the traffic
|
|
* of the same AC */
|
|
if (i % 2)
|
|
next_prio = i - 1;
|
|
else
|
|
next_prio = i + 1;
|
|
next_tid =
|
|
tos_to_tid[next_prio];
|
|
if (priv_tmp->wmm.pkts_queued
|
|
[next_tid] &&
|
|
(priv_tmp->wmm.pkts_queued
|
|
[next_tid] >
|
|
priv_tmp->wmm.pkts_paused
|
|
[next_tid]))
|
|
util_scalar_write(
|
|
pmadapter->pmoal_handle,
|
|
&priv_tmp->wmm
|
|
.highest_queued_prio,
|
|
next_prio,
|
|
MNULL, MNULL);
|
|
else
|
|
/* if
|
|
* highest_queued_prio >
|
|
* i, set it to i */
|
|
util_scalar_conditional_write(
|
|
pmadapter->pmoal_handle,
|
|
&priv_tmp->wmm
|
|
.highest_queued_prio,
|
|
MLAN_SCALAR_COND_GREATER_THAN,
|
|
i, i, MNULL,
|
|
MNULL);
|
|
*priv = priv_tmp;
|
|
*tid = tos_to_tid[i];
|
|
/* hold priv->ra_list_spinlock
|
|
* to maintain ptr */
|
|
PRINTM(MDAT_D,
|
|
"get highest prio ptr %p, tid %d\n",
|
|
ptr, *tid);
|
|
LEAVE();
|
|
return ptr;
|
|
}
|
|
|
|
ptr = ptr->pnext;
|
|
if (ptr ==
|
|
(raListTbl *)&tid_ptr->ra_list)
|
|
ptr = ptr->pnext;
|
|
} while (ptr != head);
|
|
}
|
|
|
|
/* If priv still has packets queued, reset to
|
|
* HIGH_PRIO_TID */
|
|
if (util_scalar_read(pmadapter->pmoal_handle,
|
|
&priv_tmp->wmm.tx_pkts_queued,
|
|
MNULL, MNULL))
|
|
util_scalar_write(
|
|
pmadapter->pmoal_handle,
|
|
&priv_tmp->wmm.highest_queued_prio,
|
|
HIGH_PRIO_TID, MNULL, MNULL);
|
|
else
|
|
/* No packet at any TID for this priv. Mark as
|
|
* such to skip checking TIDs for this priv
|
|
* (until pkt is added). */
|
|
util_scalar_write(
|
|
pmadapter->pmoal_handle,
|
|
&priv_tmp->wmm.highest_queued_prio,
|
|
NO_PKT_PRIO_TID, MNULL, MNULL);
|
|
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle,
|
|
priv_tmp->wmm.ra_list_spinlock);
|
|
|
|
next_intf:
|
|
bssprio_node = bssprio_node->pnext;
|
|
if (bssprio_node ==
|
|
(mlan_bssprio_node *)&pmadapter->bssprio_tbl[j]
|
|
.bssprio_head)
|
|
bssprio_node = bssprio_node->pnext;
|
|
pmadapter->bssprio_tbl[j].bssprio_cur = bssprio_node;
|
|
} 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 max_buf_size Maximum buffer size
|
|
*
|
|
* @return Packet count
|
|
*/
|
|
static int wlan_num_pkts_in_txq(mlan_private *priv, raListTbl *ptr,
|
|
int max_buf_size)
|
|
{
|
|
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 < max_buf_size)
|
|
++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
|
|
* @param ptrindex ptr's TID index
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static INLINE void 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();
|
|
|
|
pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle,
|
|
&ptr->buf_head, MNULL, MNULL);
|
|
if (pmbuf) {
|
|
PRINTM(MINFO, "Dequeuing the packet %p %p\n", ptr, pmbuf);
|
|
priv->wmm.pkts_queued[ptrindex]--;
|
|
util_scalar_decrement(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued, MNULL, MNULL);
|
|
ptr->total_pkts--;
|
|
pmbuf_next = (pmlan_buffer)util_peek_list(
|
|
pmadapter->pmoal_handle, &ptr->buf_head, MNULL, MNULL);
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle, 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(
|
|
pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
|
|
if (!wlan_is_ralist_valid(priv, ptr, ptrindex)) {
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
wlan_write_data_complete(pmadapter, pmbuf,
|
|
MLAN_STATUS_FAILURE);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
priv->wmm.pkts_queued[ptrindex]++;
|
|
util_scalar_increment(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued, MNULL,
|
|
MNULL);
|
|
util_enqueue_list_head(pmadapter->pmoal_handle,
|
|
&ptr->buf_head,
|
|
(pmlan_linked_list)pmbuf, MNULL,
|
|
MNULL);
|
|
|
|
ptr->total_pkts++;
|
|
pmbuf->flags |= MLAN_BUF_FLAG_REQUEUED_PKT;
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
} else {
|
|
pmadapter->callbacks.moal_spin_lock(
|
|
pmadapter->pmoal_handle,
|
|
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(
|
|
pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
}
|
|
} else {
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock);
|
|
PRINTM(MINFO, "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 INLINE int wlan_is_ptr_processed(mlan_private *priv, raListTbl *ptr)
|
|
{
|
|
pmlan_buffer pmbuf;
|
|
|
|
pmbuf = (pmlan_buffer)util_peek_list(priv->adapter->pmoal_handle,
|
|
&ptr->buf_head, MNULL, MNULL);
|
|
if (pmbuf && (pmbuf->flags & MLAN_BUF_FLAG_REQUEUED_PKT))
|
|
return MTRUE;
|
|
|
|
return MFALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief This function sends a single packet that has been processed
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param ptr A pointer to RA list table
|
|
* @param ptrindex ptr's TID index
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static INLINE void wlan_send_processed_packet(pmlan_private priv,
|
|
raListTbl *ptr, int ptrindex)
|
|
{
|
|
pmlan_buffer pmbuf_next = MNULL;
|
|
mlan_tx_param tx_param;
|
|
pmlan_buffer pmbuf;
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
mlan_status ret = MLAN_STATUS_FAILURE;
|
|
|
|
pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle,
|
|
&ptr->buf_head, MNULL, MNULL);
|
|
if (pmbuf) {
|
|
pmbuf_next = (pmlan_buffer)util_peek_list(
|
|
pmadapter->pmoal_handle, &ptr->buf_head, MNULL, MNULL);
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock);
|
|
tx_param.next_pkt_len =
|
|
((pmbuf_next) ? pmbuf_next->data_len + sizeof(TxPD) :
|
|
0);
|
|
|
|
ret = pmadapter->ops.host_to_card(priv, MLAN_TYPE_DATA, pmbuf,
|
|
&tx_param);
|
|
switch (ret) {
|
|
#ifdef USB
|
|
case MLAN_STATUS_PRESOURCE:
|
|
PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n");
|
|
break;
|
|
#endif
|
|
case MLAN_STATUS_RESOURCE:
|
|
PRINTM(MINFO, "MLAN_STATUS_RESOURCE is returned\n");
|
|
pmadapter->callbacks.moal_spin_lock(
|
|
pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
|
|
if (!wlan_is_ralist_valid(priv, ptr, ptrindex)) {
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
wlan_write_data_complete(pmadapter, pmbuf,
|
|
MLAN_STATUS_FAILURE);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
util_enqueue_list_head(pmadapter->pmoal_handle,
|
|
&ptr->buf_head,
|
|
(pmlan_linked_list)pmbuf, MNULL,
|
|
MNULL);
|
|
|
|
pmbuf->flags |= MLAN_BUF_FLAG_REQUEUED_PKT;
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
break;
|
|
case MLAN_STATUS_FAILURE:
|
|
PRINTM(MERROR, "Error: Failed to write data\n");
|
|
pmadapter->dbg.num_tx_host_to_card_failure++;
|
|
pmbuf->status_code = MLAN_ERROR_DATA_TX_FAIL;
|
|
wlan_write_data_complete(pmadapter, pmbuf, ret);
|
|
break;
|
|
case MLAN_STATUS_PENDING:
|
|
break;
|
|
case MLAN_STATUS_SUCCESS:
|
|
DBG_HEXDUMP(MDAT_D, "Tx",
|
|
pmbuf->pbuf + pmbuf->data_offset,
|
|
MIN(pmbuf->data_len + sizeof(TxPD),
|
|
MAX_DATA_DUMP_LEN));
|
|
wlan_write_data_complete(pmadapter, pmbuf, 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, ptr, ptrindex)) {
|
|
priv->wmm.packets_out[ptrindex]++;
|
|
priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr =
|
|
ptr;
|
|
ptr->total_pkts--;
|
|
}
|
|
pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur =
|
|
pmadapter->bssprio_tbl[priv->bss_priority]
|
|
.bssprio_cur->pnext;
|
|
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);
|
|
}
|
|
} else {
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief This function dequeues a packet
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
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_del = 0;
|
|
int tid = 0;
|
|
mlan_buffer *pmbuf = MNULL;
|
|
|
|
ENTER();
|
|
|
|
ptr = wlan_wmm_get_highest_priolist_ptr(pmadapter, &priv, &ptrindex);
|
|
if (!ptr) {
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
|
|
/* Note:- Spinlock is locked in wlan_wmm_get_highest_priolist_ptr
|
|
* when it returns a pointer (for the priv it returns),
|
|
* and is 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. */
|
|
|
|
tid = wlan_get_tid(priv->adapter, ptr);
|
|
if (tid >= MAX_NUM_TID)
|
|
tid = wlan_wmm_downgrade_tid(priv, tid);
|
|
|
|
if (wlan_is_ptr_processed(priv, ptr)) {
|
|
wlan_send_processed_packet(priv, ptr, ptrindex);
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
if (ptr->del_ba_count >= DEL_BA_THRESHOLD)
|
|
wlan_update_del_ba_count(priv, ptr);
|
|
if (pmadapter->tp_state_on) {
|
|
pmbuf = (pmlan_buffer)util_peek_list(
|
|
pmadapter->pmoal_handle, &ptr->buf_head, MNULL, MNULL);
|
|
if (pmbuf) {
|
|
pmadapter->callbacks.moal_tp_accounting(
|
|
pmadapter->pmoal_handle, pmbuf, 3);
|
|
if (pmadapter->tp_state_drop_point == 3) {
|
|
pmbuf = (pmlan_buffer)util_dequeue_list(
|
|
pmadapter->pmoal_handle, &ptr->buf_head,
|
|
MNULL, MNULL);
|
|
PRINTM(MERROR, "Dequeuing the packet %p %p\n",
|
|
ptr, pmbuf);
|
|
priv->wmm.pkts_queued[ptrindex]--;
|
|
util_scalar_decrement(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued,
|
|
MNULL, MNULL);
|
|
ptr->total_pkts--;
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
wlan_write_data_complete(pmadapter, pmbuf,
|
|
MLAN_STATUS_SUCCESS);
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
if (!ptr->is_wmm_enabled || priv->adapter->remain_on_channel ||
|
|
(ptr->ba_status || ptr->del_ba_count >= DEL_BA_THRESHOLD)
|
|
#ifdef STA_SUPPORT
|
|
|| priv->wps.session_enable
|
|
#endif /* STA_SUPPORT */
|
|
) {
|
|
if (ptr->is_wmm_enabled && ptr->ba_status &&
|
|
ptr->amsdu_in_ampdu &&
|
|
wlan_is_amsdu_allowed(priv, ptr, tid) &&
|
|
(wlan_num_pkts_in_txq(priv, ptr, pmadapter->tx_buf_size) >=
|
|
MIN_NUM_AMSDU)) {
|
|
wlan_11n_aggregate_pkt(priv, ptr, priv->intf_hr_len,
|
|
ptrindex);
|
|
} else
|
|
wlan_send_single_packet(priv, ptr, ptrindex);
|
|
} else {
|
|
if (wlan_is_ampdu_allowed(priv, ptr, tid) &&
|
|
(ptr->packet_count > ptr->ba_packet_threshold)) {
|
|
if (wlan_is_bastream_avail(priv)) {
|
|
PRINTM(MINFO,
|
|
"BA setup threshold %d reached. tid=%d\n",
|
|
ptr->packet_count, tid);
|
|
if (!wlan_11n_get_txbastream_tbl(
|
|
priv, tid, ptr->ra, MFALSE)) {
|
|
wlan_11n_create_txbastream_tbl(
|
|
priv, ptr->ra, tid,
|
|
BA_STREAM_SETUP_SENT_ADDBA);
|
|
wlan_send_addba(priv, tid, ptr->ra);
|
|
}
|
|
} else if (wlan_find_stream_to_delete(priv, ptr, tid,
|
|
&tid_del, ra)) {
|
|
PRINTM(MDAT_D, "tid_del=%d tid=%d\n", tid_del,
|
|
tid);
|
|
if (!wlan_11n_get_txbastream_tbl(
|
|
priv, tid, ptr->ra, MFALSE)) {
|
|
wlan_11n_create_txbastream_tbl(
|
|
priv, ptr->ra, tid,
|
|
BA_STREAM_SETUP_INPROGRESS);
|
|
wlan_11n_set_txbastream_status(
|
|
priv, tid_del, ra,
|
|
BA_STREAM_SENT_DELBA, MFALSE);
|
|
wlan_send_delba(priv, MNULL, tid_del,
|
|
ra, 1);
|
|
}
|
|
}
|
|
}
|
|
if (wlan_is_amsdu_allowed(priv, ptr, tid) &&
|
|
(wlan_num_pkts_in_txq(priv, ptr, pmadapter->tx_buf_size) >=
|
|
MIN_NUM_AMSDU)) {
|
|
wlan_11n_aggregate_pkt(priv, ptr, priv->intf_hr_len,
|
|
ptrindex);
|
|
} else {
|
|
wlan_send_single_packet(priv, ptr, ptrindex);
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief update tx_pause flag in ra_list
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param mac peer mac address
|
|
* @param tx_pause tx_pause flag (0/1)
|
|
*
|
|
*
|
|
* @return packets queued for this mac
|
|
*/
|
|
t_u16 wlan_update_ralist_tx_pause(pmlan_private priv, t_u8 *mac, t_u8 tx_pause)
|
|
{
|
|
raListTbl *ra_list;
|
|
int i;
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
t_u32 pkt_cnt = 0;
|
|
t_u32 tx_pkts_queued = 0;
|
|
ENTER();
|
|
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
for (i = 0; i < MAX_NUM_TID; ++i) {
|
|
ra_list = wlan_wmm_get_ralist_node(priv, i, mac);
|
|
if (ra_list && ra_list->tx_pause != tx_pause) {
|
|
pkt_cnt += ra_list->total_pkts;
|
|
ra_list->tx_pause = tx_pause;
|
|
if (tx_pause)
|
|
priv->wmm.pkts_paused[i] += ra_list->total_pkts;
|
|
else
|
|
priv->wmm.pkts_paused[i] -= ra_list->total_pkts;
|
|
}
|
|
}
|
|
if (pkt_cnt) {
|
|
tx_pkts_queued = util_scalar_read(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued,
|
|
MNULL, MNULL);
|
|
if (tx_pause)
|
|
tx_pkts_queued -= pkt_cnt;
|
|
else
|
|
tx_pkts_queued += pkt_cnt;
|
|
util_scalar_write(priv->adapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued, tx_pkts_queued,
|
|
MNULL, MNULL);
|
|
util_scalar_write(priv->adapter->pmoal_handle,
|
|
&priv->wmm.highest_queued_prio, HIGH_PRIO_TID,
|
|
MNULL, MNULL);
|
|
}
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
LEAVE();
|
|
return pkt_cnt;
|
|
}
|
|
|
|
#ifdef STA_SUPPORT
|
|
/**
|
|
* @brief update tx_pause flag in none tdls ra_list
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param mac peer mac address
|
|
* @param tx_pause tx_pause flag (0/1)
|
|
*
|
|
* @return N/A
|
|
*/
|
|
t_void wlan_update_non_tdls_ralist(mlan_private *priv, t_u8 *mac, t_u8 tx_pause)
|
|
{
|
|
raListTbl *ra_list;
|
|
int i;
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
t_u32 pkt_cnt = 0;
|
|
t_u32 tx_pkts_queued = 0;
|
|
ENTER();
|
|
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
for (i = 0; i < MAX_NUM_TID; ++i) {
|
|
ra_list = (raListTbl *)util_peek_list(
|
|
priv->adapter->pmoal_handle,
|
|
&priv->wmm.tid_tbl_ptr[i].ra_list, MNULL, MNULL);
|
|
while (ra_list &&
|
|
(ra_list !=
|
|
(raListTbl *)&priv->wmm.tid_tbl_ptr[i].ra_list)) {
|
|
if (memcmp(priv->adapter, ra_list->ra, mac,
|
|
MLAN_MAC_ADDR_LENGTH) &&
|
|
ra_list->tx_pause != tx_pause) {
|
|
pkt_cnt += ra_list->total_pkts;
|
|
ra_list->tx_pause = tx_pause;
|
|
if (tx_pause)
|
|
priv->wmm.pkts_paused[i] +=
|
|
ra_list->total_pkts;
|
|
else
|
|
priv->wmm.pkts_paused[i] -=
|
|
ra_list->total_pkts;
|
|
}
|
|
ra_list = ra_list->pnext;
|
|
}
|
|
}
|
|
if (pkt_cnt) {
|
|
tx_pkts_queued = util_scalar_read(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued,
|
|
MNULL, MNULL);
|
|
if (tx_pause)
|
|
tx_pkts_queued -= pkt_cnt;
|
|
else
|
|
tx_pkts_queued += pkt_cnt;
|
|
util_scalar_write(priv->adapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued, tx_pkts_queued,
|
|
MNULL, MNULL);
|
|
util_scalar_write(priv->adapter->pmoal_handle,
|
|
&priv->wmm.highest_queued_prio, HIGH_PRIO_TID,
|
|
MNULL, MNULL);
|
|
}
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief find tdls buffer from ralist
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param ralist A pointer to ralistTbl
|
|
* @param mac TDLS peer mac address
|
|
*
|
|
* @return pmlan_buffer or MNULL
|
|
*/
|
|
static pmlan_buffer wlan_find_tdls_packets(mlan_private *priv,
|
|
raListTbl *ra_list, t_u8 *mac)
|
|
{
|
|
pmlan_buffer pmbuf = MNULL;
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
t_u8 ra[MLAN_MAC_ADDR_LENGTH];
|
|
ENTER();
|
|
pmbuf = (pmlan_buffer)util_peek_list(priv->adapter->pmoal_handle,
|
|
&ra_list->buf_head, MNULL, MNULL);
|
|
if (!pmbuf) {
|
|
LEAVE();
|
|
return MNULL;
|
|
}
|
|
while (pmbuf != (pmlan_buffer)&ra_list->buf_head) {
|
|
memcpy_ext(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
if (!memcmp(priv->adapter, ra, mac, MLAN_MAC_ADDR_LENGTH)) {
|
|
LEAVE();
|
|
return pmbuf;
|
|
}
|
|
pmbuf = pmbuf->pnext;
|
|
}
|
|
LEAVE();
|
|
return MNULL;
|
|
}
|
|
|
|
/**
|
|
* @brief find tdls buffer from tdls pending queue
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param mac TDLS peer mac address
|
|
*
|
|
* @return pmlan_buffer or MNULL
|
|
*/
|
|
static pmlan_buffer wlan_find_packets_tdls_txq(mlan_private *priv, t_u8 *mac)
|
|
{
|
|
pmlan_buffer pmbuf = MNULL;
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
t_u8 ra[MLAN_MAC_ADDR_LENGTH];
|
|
ENTER();
|
|
pmbuf = (pmlan_buffer)util_peek_list(priv->adapter->pmoal_handle,
|
|
&priv->tdls_pending_txq, MNULL,
|
|
MNULL);
|
|
if (!pmbuf) {
|
|
LEAVE();
|
|
return MNULL;
|
|
}
|
|
while (pmbuf != (pmlan_buffer)&priv->tdls_pending_txq) {
|
|
memcpy_ext(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
if (!memcmp(priv->adapter, ra, mac, MLAN_MAC_ADDR_LENGTH)) {
|
|
LEAVE();
|
|
return pmbuf;
|
|
}
|
|
pmbuf = pmbuf->pnext;
|
|
}
|
|
LEAVE();
|
|
return MNULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Remove TDLS ralist and move packets to AP's ralist
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param mac TDLS peer mac address
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static t_void wlan_wmm_delete_tdls_ralist(pmlan_private priv, t_u8 *mac)
|
|
{
|
|
raListTbl *ra_list;
|
|
raListTbl *ra_list_ap = MNULL;
|
|
int i;
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
pmlan_buffer pmbuf;
|
|
ENTER();
|
|
|
|
for (i = 0; i < MAX_NUM_TID; ++i) {
|
|
ra_list = wlan_wmm_get_ralist_node(priv, i, mac);
|
|
if (ra_list) {
|
|
PRINTM(MDATA, "delete TDLS ralist %p\n", ra_list);
|
|
ra_list_ap = (raListTbl *)util_peek_list(
|
|
pmadapter->pmoal_handle,
|
|
&priv->wmm.tid_tbl_ptr[i].ra_list, MNULL,
|
|
MNULL);
|
|
if (!ra_list_ap) {
|
|
LEAVE();
|
|
return;
|
|
}
|
|
while ((pmbuf = (pmlan_buffer)util_peek_list(
|
|
pmadapter->pmoal_handle,
|
|
&ra_list->buf_head, MNULL, MNULL))) {
|
|
util_unlink_list(pmadapter->pmoal_handle,
|
|
&ra_list->buf_head,
|
|
(pmlan_linked_list)pmbuf,
|
|
MNULL, MNULL);
|
|
util_enqueue_list_tail(pmadapter->pmoal_handle,
|
|
&ra_list_ap->buf_head,
|
|
(pmlan_linked_list)pmbuf,
|
|
MNULL, MNULL);
|
|
ra_list_ap->total_pkts++;
|
|
ra_list_ap->packet_count++;
|
|
}
|
|
util_free_list_head(
|
|
(t_void *)pmadapter->pmoal_handle,
|
|
&ra_list->buf_head,
|
|
pmadapter->callbacks.moal_free_lock);
|
|
|
|
util_unlink_list(pmadapter->pmoal_handle,
|
|
&priv->wmm.tid_tbl_ptr[i].ra_list,
|
|
(pmlan_linked_list)ra_list, MNULL,
|
|
MNULL);
|
|
pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle,
|
|
(t_u8 *)ra_list);
|
|
if (priv->wmm.tid_tbl_ptr[i].ra_list_curr == ra_list)
|
|
priv->wmm.tid_tbl_ptr[i].ra_list_curr =
|
|
ra_list_ap;
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
/********************************************************
|
|
Global Functions
|
|
********************************************************/
|
|
|
|
/**
|
|
* @brief Get the threshold value for BA setup using system time.
|
|
*
|
|
* @param pmadapter Pointer to the mlan_adapter structure
|
|
*
|
|
* @return threshold value.
|
|
*/
|
|
t_u8 wlan_get_random_ba_threshold(pmlan_adapter pmadapter)
|
|
{
|
|
t_u32 sec, usec;
|
|
t_u8 ba_threshold = 0;
|
|
|
|
ENTER();
|
|
|
|
/* setup ba_packet_threshold here random number between
|
|
[BA_SETUP_PACKET_OFFSET,
|
|
BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] */
|
|
|
|
#define BA_SETUP_MAX_PACKET_THRESHOLD 16
|
|
|
|
pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec,
|
|
&usec);
|
|
sec = (sec & 0xFFFF) + (sec >> 16);
|
|
usec = (usec & 0xFFFF) + (usec >> 16);
|
|
|
|
ba_threshold =
|
|
(t_u8)((((sec << 16) + usec) % BA_SETUP_MAX_PACKET_THRESHOLD) +
|
|
pmadapter->min_ba_threshold);
|
|
PRINTM(MINFO, "pmadapter->min_ba_threshold = %d\n",
|
|
pmadapter->min_ba_threshold);
|
|
PRINTM(MINFO, "setup BA after %d packets\n", ba_threshold);
|
|
|
|
LEAVE();
|
|
return ba_threshold;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
t_u8 i = 0;
|
|
|
|
ENTER();
|
|
wlan_cleanup_bypass_txq(priv);
|
|
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
|
|
wlan_cleanup_tdls_txq(priv);
|
|
}
|
|
wlan_11n_cleanup_reorder_tbl(priv);
|
|
wlan_11n_deleteall_txbastream_tbl(priv);
|
|
#if defined(USB)
|
|
if (IS_USB(pmadapter->card_type))
|
|
wlan_reset_usb_tx_aggr(priv->adapter);
|
|
#endif
|
|
#ifdef PCIE
|
|
if (IS_PCIE(pmadapter->card_type))
|
|
wlan_clean_pcie_ring_buf(priv->adapter);
|
|
#endif
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
wlan_wmm_cleanup_queues(priv);
|
|
wlan_wmm_delete_all_ralist(priv);
|
|
memcpy_ext(pmadapter, tos_to_tid, ac_to_tid, sizeof(tos_to_tid),
|
|
sizeof(tos_to_tid));
|
|
for (i = 0; i < MAX_NUM_TID; i++)
|
|
tos_to_tid_inv[tos_to_tid[i]] = (t_u8)i;
|
|
#ifdef UAP_SUPPORT
|
|
priv->num_drop_pkts = 0;
|
|
#endif
|
|
#ifdef SDIO
|
|
if (IS_SD(pmadapter->card_type)) {
|
|
memset(pmadapter, pmadapter->pcard_sd->mpa_tx_count, 0,
|
|
sizeof(pmadapter->pcard_sd->mpa_tx_count));
|
|
pmadapter->pcard_sd->mpa_sent_no_ports = 0;
|
|
pmadapter->pcard_sd->mpa_sent_last_pkt = 0;
|
|
memset(pmadapter, pmadapter->pcard_sd->mpa_rx_count, 0,
|
|
sizeof(pmadapter->pcard_sd->mpa_rx_count));
|
|
}
|
|
#endif
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
|
|
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 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;
|
|
}
|
|
memset(priv->adapter, tmp, 0, sizeof(tmp));
|
|
|
|
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(MCMND, "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));
|
|
DBG_HEXDUMP(MCMD_D, "WMM: queue_priority, sort",
|
|
priv->wmm.queue_priority, sizeof(priv->wmm.queue_priority));
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @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++) {
|
|
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 This function checks whether a station has WMM enabled or not
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param mac station mac address
|
|
* @return MTRUE or MFALSE
|
|
*/
|
|
static t_u8 is_station_wmm_enabled(mlan_private *priv, t_u8 *mac)
|
|
{
|
|
sta_node *sta_ptr = MNULL;
|
|
sta_ptr = wlan_get_station_entry(priv, mac);
|
|
if (sta_ptr) {
|
|
if (sta_ptr->is_11n_enabled || sta_ptr->is_11ax_enabled)
|
|
return MTRUE;
|
|
}
|
|
return MFALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief This function checks whether wmm is supported
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param ra Address of the receiver STA
|
|
*
|
|
* @return MTRUE or MFALSE
|
|
*/
|
|
static int wlan_is_wmm_enabled(mlan_private *priv, t_u8 *ra)
|
|
{
|
|
int ret = MFALSE;
|
|
ENTER();
|
|
#ifdef UAP_SUPPORT
|
|
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
|
|
if ((!(ra[0] & 0x01)) &&
|
|
(priv->is_11n_enabled || priv->is_11ax_enabled))
|
|
ret = is_station_wmm_enabled(priv, ra);
|
|
}
|
|
#endif /* UAP_SUPPORT */
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
tdlsStatus_e status;
|
|
|
|
ENTER();
|
|
|
|
for (i = 0; i < MAX_NUM_TID; ++i) {
|
|
ra_list = wlan_wmm_allocate_ralist_node(pmadapter, ra);
|
|
PRINTM(MINFO, "Creating RA List %p for tid %d\n", ra_list, i);
|
|
if (!ra_list)
|
|
break;
|
|
ra_list->max_amsdu = 0;
|
|
ra_list->ba_status = BA_STREAM_NOT_SETUP;
|
|
ra_list->amsdu_in_ampdu = MFALSE;
|
|
if (queuing_ra_based(priv)) {
|
|
ra_list->is_wmm_enabled = wlan_is_wmm_enabled(priv, ra);
|
|
if (ra_list->is_wmm_enabled)
|
|
ra_list->max_amsdu =
|
|
get_station_max_amsdu_size(priv, ra);
|
|
ra_list->tx_pause = wlan_is_tx_pause(priv, ra);
|
|
} else {
|
|
ra_list->is_tdls_link = MFALSE;
|
|
ra_list->tx_pause = MFALSE;
|
|
status = wlan_get_tdls_link_status(priv, ra);
|
|
if (MTRUE == wlan_is_tdls_link_setup(status)) {
|
|
ra_list->is_wmm_enabled =
|
|
is_station_wmm_enabled(priv, ra);
|
|
if (ra_list->is_wmm_enabled)
|
|
ra_list->max_amsdu =
|
|
get_station_max_amsdu_size(priv,
|
|
ra);
|
|
ra_list->is_tdls_link = MTRUE;
|
|
} else {
|
|
ra_list->is_wmm_enabled = IS_11N_ENABLED(priv);
|
|
if (ra_list->is_wmm_enabled)
|
|
ra_list->max_amsdu = priv->max_amsdu;
|
|
}
|
|
}
|
|
|
|
PRINTM_NETINTF(MDATA, priv);
|
|
PRINTM(MDATA, "ralist %p: is_wmm_enabled=%d max_amsdu=%d\n",
|
|
ra_list, ra_list->is_wmm_enabled, ra_list->max_amsdu);
|
|
|
|
if (ra_list->is_wmm_enabled) {
|
|
ra_list->packet_count = 0;
|
|
ra_list->ba_packet_threshold =
|
|
wlan_get_random_ba_threshold(pmadapter);
|
|
}
|
|
|
|
util_enqueue_list_tail(pmadapter->pmoal_handle,
|
|
&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 Initialize the WMM parameter.
|
|
*
|
|
* @param pmadapter Pointer to the mlan_adapter data structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
t_void wlan_init_wmm_param(pmlan_adapter pmadapter)
|
|
{
|
|
/* Reuse the same structure of WmmAcParameters_t for configuration
|
|
* purpose here. the definition of acm bit is changed to ucm (user
|
|
* configuration mode) FW will take the setting of
|
|
* aifsn,ecw_max,ecw_min, tx_op_limit only when ucm is set to 1.
|
|
* othewise the default setting/behavoir in firmware will be used.
|
|
*/
|
|
pmadapter->ac_params[AC_BE].aci_aifsn.acm = 0;
|
|
pmadapter->ac_params[AC_BE].aci_aifsn.aci = AC_BE;
|
|
pmadapter->ac_params[AC_BE].aci_aifsn.aifsn = 3;
|
|
pmadapter->ac_params[AC_BE].ecw.ecw_max = 10;
|
|
pmadapter->ac_params[AC_BE].ecw.ecw_min = 4;
|
|
pmadapter->ac_params[AC_BE].tx_op_limit = 0;
|
|
|
|
pmadapter->ac_params[AC_BK].aci_aifsn.acm = 0;
|
|
pmadapter->ac_params[AC_BK].aci_aifsn.aci = AC_BK;
|
|
pmadapter->ac_params[AC_BK].aci_aifsn.aifsn = 7;
|
|
pmadapter->ac_params[AC_BK].ecw.ecw_max = 10;
|
|
pmadapter->ac_params[AC_BK].ecw.ecw_min = 4;
|
|
pmadapter->ac_params[AC_BK].tx_op_limit = 0;
|
|
|
|
pmadapter->ac_params[AC_VI].aci_aifsn.acm = 0;
|
|
pmadapter->ac_params[AC_VI].aci_aifsn.aci = AC_VI;
|
|
pmadapter->ac_params[AC_VI].aci_aifsn.aifsn = 2;
|
|
pmadapter->ac_params[AC_VI].ecw.ecw_max = 4;
|
|
pmadapter->ac_params[AC_VI].ecw.ecw_min = 3;
|
|
pmadapter->ac_params[AC_VI].tx_op_limit = 188;
|
|
|
|
pmadapter->ac_params[AC_VO].aci_aifsn.acm = 0;
|
|
pmadapter->ac_params[AC_VO].aci_aifsn.aci = AC_VO;
|
|
pmadapter->ac_params[AC_VO].aci_aifsn.aifsn = 2;
|
|
pmadapter->ac_params[AC_VO].ecw.ecw_max = 3;
|
|
pmadapter->ac_params[AC_VO].ecw.ecw_min = 2;
|
|
pmadapter->ac_params[AC_VO].tx_op_limit = 102;
|
|
}
|
|
|
|
/**
|
|
* @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 < pmadapter->priv_num; ++j) {
|
|
priv = pmadapter->priv[j];
|
|
if (priv) {
|
|
for (i = 0; i < MAX_NUM_TID; ++i) {
|
|
priv->aggr_prio_tbl[i].amsdu =
|
|
tos_to_tid_inv[i];
|
|
priv->aggr_prio_tbl[i].ampdu_ap =
|
|
priv->aggr_prio_tbl[i].ampdu_user =
|
|
tos_to_tid_inv[i];
|
|
priv->ibss_ampdu[i] =
|
|
priv->aggr_prio_tbl[i].ampdu_user;
|
|
priv->wmm.pkts_queued[i] = 0;
|
|
priv->wmm.pkts_paused[i] = 0;
|
|
priv->wmm.tid_tbl_ptr[i].ra_list_curr = MNULL;
|
|
}
|
|
priv->wmm.drv_pkt_delay_max = WMM_DRV_DELAY_MAX;
|
|
|
|
priv->aggr_prio_tbl[6].amsdu = BA_STREAM_NOT_ALLOWED;
|
|
priv->aggr_prio_tbl[7].amsdu = BA_STREAM_NOT_ALLOWED;
|
|
priv->aggr_prio_tbl[6].ampdu_ap =
|
|
priv->aggr_prio_tbl[6].ampdu_user =
|
|
BA_STREAM_NOT_ALLOWED;
|
|
priv->ibss_ampdu[6] = BA_STREAM_NOT_ALLOWED;
|
|
|
|
priv->aggr_prio_tbl[7].ampdu_ap =
|
|
priv->aggr_prio_tbl[7].ampdu_user =
|
|
BA_STREAM_NOT_ALLOWED;
|
|
priv->ibss_ampdu[7] = BA_STREAM_NOT_ALLOWED;
|
|
|
|
priv->add_ba_param.timeout =
|
|
MLAN_DEFAULT_BLOCK_ACK_TIMEOUT;
|
|
#ifdef STA_SUPPORT
|
|
if (priv->bss_type == MLAN_BSS_TYPE_STA) {
|
|
priv->add_ba_param.tx_win_size =
|
|
MLAN_STA_AMPDU_DEF_TXWINSIZE;
|
|
priv->add_ba_param.rx_win_size =
|
|
MLAN_STA_AMPDU_DEF_RXWINSIZE;
|
|
}
|
|
#endif
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
|
|
priv->add_ba_param.tx_win_size =
|
|
MLAN_WFD_AMPDU_DEF_TXRXWINSIZE;
|
|
priv->add_ba_param.rx_win_size =
|
|
MLAN_WFD_AMPDU_DEF_TXRXWINSIZE;
|
|
}
|
|
#endif
|
|
#ifdef UAP_SUPPORT
|
|
if (priv->bss_type == MLAN_BSS_TYPE_UAP) {
|
|
priv->add_ba_param.tx_win_size =
|
|
MLAN_UAP_AMPDU_DEF_TXWINSIZE;
|
|
priv->add_ba_param.rx_win_size =
|
|
MLAN_UAP_AMPDU_DEF_RXWINSIZE;
|
|
}
|
|
#endif
|
|
priv->user_rxwinsize = priv->add_ba_param.rx_win_size;
|
|
priv->add_ba_param.tx_amsdu = MTRUE;
|
|
priv->add_ba_param.rx_amsdu = MTRUE;
|
|
memset(priv->adapter, priv->rx_seq, 0xff,
|
|
sizeof(priv->rx_seq));
|
|
wlan_wmm_default_queue_priorities(priv);
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
#ifdef STA_SUPPORT
|
|
/**
|
|
* @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
|
|
*/
|
|
mlan_status wlan_cmd_wmm_status_change(pmlan_private priv)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
ret = wlan_prepare_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, 0, 0, MNULL,
|
|
MNULL);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @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 j;
|
|
pmlan_private priv;
|
|
|
|
ENTER();
|
|
|
|
for (j = 0; j < pmadapter->priv_num; ++j) {
|
|
priv = pmadapter->priv[j];
|
|
if (priv) {
|
|
if ((priv->port_ctrl_mode == MTRUE) &&
|
|
(priv->port_open == MFALSE)) {
|
|
PRINTM(MINFO,
|
|
"wmm_lists_empty: PORT_CLOSED Ignore pkts from BSS%d\n",
|
|
j);
|
|
continue;
|
|
}
|
|
if (priv->tx_pause)
|
|
continue;
|
|
#if defined(USB)
|
|
if (!wlan_is_port_ready(pmadapter, priv->port_index))
|
|
continue;
|
|
#endif
|
|
|
|
if (util_scalar_read(
|
|
pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued,
|
|
pmadapter->callbacks.moal_spin_lock,
|
|
pmadapter->callbacks.moal_spin_unlock)) {
|
|
LEAVE();
|
|
return MFALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return MTRUE;
|
|
}
|
|
|
|
/**
|
|
* @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->adapter->pmoal_handle,
|
|
&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(priv->adapter, ra_list->ra, ra_addr,
|
|
MLAN_MAC_ADDR_LENGTH)) {
|
|
LEAVE();
|
|
return ra_list;
|
|
}
|
|
ra_list = ra_list->pnext;
|
|
}
|
|
LEAVE();
|
|
return MNULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if RA list is valid or not
|
|
*
|
|
* @param priv Pointer to the mlan_private driver data struct
|
|
* @param ra_list Pointer to raListTbl
|
|
* @param ptrindex TID pointer index
|
|
*
|
|
* @return MTRUE- valid. MFALSE- invalid.
|
|
*/
|
|
int wlan_is_ralist_valid(mlan_private *priv, raListTbl *ra_list, int ptrindex)
|
|
{
|
|
raListTbl *rlist;
|
|
|
|
ENTER();
|
|
|
|
rlist = (raListTbl *)util_peek_list(
|
|
priv->adapter->pmoal_handle,
|
|
&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) {
|
|
LEAVE();
|
|
return MTRUE;
|
|
}
|
|
|
|
rlist = rlist->pnext;
|
|
}
|
|
LEAVE();
|
|
return MFALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Update an existing raList with a new RA and 11n capability
|
|
*
|
|
* @param priv Pointer to the mlan_private driver data struct
|
|
* @param old_ra Old receiver address
|
|
* @param new_ra New receiver address
|
|
*
|
|
* @return integer count of updated nodes
|
|
*/
|
|
int wlan_ralist_update(mlan_private *priv, t_u8 *old_ra, t_u8 *new_ra)
|
|
{
|
|
t_u8 tid;
|
|
int update_count;
|
|
raListTbl *ra_list;
|
|
|
|
ENTER();
|
|
|
|
update_count = 0;
|
|
|
|
for (tid = 0; tid < MAX_NUM_TID; ++tid) {
|
|
ra_list = wlan_wmm_get_ralist_node(priv, tid, old_ra);
|
|
|
|
if (ra_list) {
|
|
update_count++;
|
|
|
|
if (queuing_ra_based(priv)) {
|
|
ra_list->is_wmm_enabled =
|
|
wlan_is_wmm_enabled(priv, new_ra);
|
|
if (ra_list->is_wmm_enabled)
|
|
ra_list->max_amsdu =
|
|
get_station_max_amsdu_size(
|
|
priv, new_ra);
|
|
} else {
|
|
ra_list->is_wmm_enabled = IS_11N_ENABLED(priv);
|
|
if (ra_list->is_wmm_enabled)
|
|
ra_list->max_amsdu = priv->max_amsdu;
|
|
}
|
|
|
|
ra_list->tx_pause = MFALSE;
|
|
ra_list->packet_count = 0;
|
|
ra_list->ba_packet_threshold =
|
|
wlan_get_random_ba_threshold(priv->adapter);
|
|
ra_list->amsdu_in_ampdu = MFALSE;
|
|
ra_list->ba_status = BA_STREAM_NOT_SETUP;
|
|
PRINTM(MINFO,
|
|
"ralist_update: %p, %d, " MACSTR "-->" MACSTR
|
|
"\n",
|
|
ra_list, ra_list->is_wmm_enabled,
|
|
MAC2STR(ra_list->ra), MAC2STR(new_ra));
|
|
|
|
memcpy_ext(priv->adapter, ra_list->ra, new_ra,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return update_count;
|
|
}
|
|
|
|
/**
|
|
* @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_index];
|
|
t_u32 tid;
|
|
raListTbl *ra_list;
|
|
t_u8 ra[MLAN_MAC_ADDR_LENGTH], tid_down;
|
|
tdlsStatus_e status;
|
|
#ifdef UAP_SUPPORT
|
|
psta_node sta_ptr = MNULL;
|
|
#endif
|
|
|
|
ENTER();
|
|
|
|
pmbuf->buf_type = MLAN_BUF_TYPE_DATA;
|
|
if (!priv->media_connected) {
|
|
PRINTM_NETINTF(MWARN, priv);
|
|
PRINTM(MWARN, "Drop packet %p in disconnect state\n", pmbuf);
|
|
wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
tid = pmbuf->priority;
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
tid_down = wlan_wmm_downgrade_tid(priv, tid);
|
|
|
|
/* 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)) {
|
|
memcpy_ext(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
status = wlan_get_tdls_link_status(priv, ra);
|
|
if (MTRUE == wlan_is_tdls_link_setup(status)) {
|
|
ra_list = wlan_wmm_get_queue_raptr(priv, tid_down, ra);
|
|
pmbuf->flags |= MLAN_BUF_FLAG_TDLS;
|
|
} else if (status == TDLS_SETUP_INPROGRESS) {
|
|
wlan_add_buf_tdls_txqueue(priv, pmbuf);
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
LEAVE();
|
|
return;
|
|
} else
|
|
ra_list = (raListTbl *)util_peek_list(
|
|
pmadapter->pmoal_handle,
|
|
&priv->wmm.tid_tbl_ptr[tid_down].ra_list, MNULL,
|
|
MNULL);
|
|
} else {
|
|
memcpy_ext(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
/** put multicast/broadcast packet in the same ralist */
|
|
if (ra[0] & 0x01)
|
|
memset(pmadapter, ra, 0xff, sizeof(ra));
|
|
#ifdef UAP_SUPPORT
|
|
else if (priv->bss_type == MLAN_BSS_TYPE_UAP) {
|
|
sta_ptr = wlan_get_station_entry(priv, ra);
|
|
if (sta_ptr) {
|
|
sta_ptr->stats.tx_bytes += pmbuf->data_len;
|
|
sta_ptr->stats.tx_packets++;
|
|
if (!sta_ptr->is_wmm_enabled &&
|
|
!priv->is_11ac_enabled) {
|
|
tid_down = wlan_wmm_downgrade_tid(priv,
|
|
0xff);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
ra_list = wlan_wmm_get_queue_raptr(priv, tid_down, ra);
|
|
}
|
|
|
|
if (!ra_list) {
|
|
PRINTM_NETINTF(MWARN, priv);
|
|
PRINTM(MWARN,
|
|
"Drop packet %p, ra_list=%p, media_connected=%d\n",
|
|
pmbuf, ra_list, priv->media_connected);
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock);
|
|
wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
PRINTM_NETINTF(MDATA, priv);
|
|
PRINTM(MDATA,
|
|
"Adding pkt %p (priority=%d, tid_down=%d) to ra_list %p\n",
|
|
pmbuf, pmbuf->priority, tid_down, ra_list);
|
|
util_enqueue_list_tail(pmadapter->pmoal_handle, &ra_list->buf_head,
|
|
(pmlan_linked_list)pmbuf, MNULL, MNULL);
|
|
|
|
ra_list->total_pkts++;
|
|
ra_list->packet_count++;
|
|
|
|
priv->wmm.pkts_queued[tid_down]++;
|
|
if (ra_list->tx_pause) {
|
|
priv->wmm.pkts_paused[tid_down]++;
|
|
} else {
|
|
util_scalar_increment(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued, MNULL, MNULL);
|
|
/* if highest_queued_prio < prio(tid_down), set it to
|
|
* prio(tid_down) */
|
|
util_scalar_conditional_write(
|
|
pmadapter->pmoal_handle, &priv->wmm.highest_queued_prio,
|
|
MLAN_SCALAR_COND_LESS_THAN, tos_to_tid_inv[tid_down],
|
|
tos_to_tid_inv[tid_down], MNULL, MNULL);
|
|
}
|
|
/* 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_get_system_time(
|
|
pmadapter->pmoal_handle, &pmbuf->in_ts_sec, &pmbuf->in_ts_usec);
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
#ifdef STA_SUPPORT
|
|
/**
|
|
* @brief Process the GET_WMM_STATUS command response from firmware
|
|
*
|
|
* The GET_WMM_STATUS response may contain multiple TLVs for:
|
|
* - AC Queue status TLVs
|
|
* - Current WMM Parameter IE TLV
|
|
* - Admission Control action frame TLVs
|
|
*
|
|
* 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 ptlv Pointer to the tlv block returned in the response.
|
|
* @param resp_len Length of TLV block
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_ret_wmm_get_status(pmlan_private priv, t_u8 *ptlv,
|
|
int resp_len)
|
|
{
|
|
t_u8 *pcurrent = ptlv;
|
|
t_u32 tlv_len;
|
|
t_u8 send_wmm_event;
|
|
MrvlIEtypes_Data_t *ptlv_hdr;
|
|
MrvlIEtypes_WmmQueueStatus_t *ptlv_wmm_q_status;
|
|
IEEEtypes_WmmParameter_t *pwmm_param_ie = MNULL;
|
|
WmmAcStatus_t *pac_status;
|
|
|
|
MrvlIETypes_ActionFrame_t *ptlv_action;
|
|
IEEEtypes_Action_WMM_AddTsRsp_t *padd_ts_rsp;
|
|
IEEEtypes_Action_WMM_DelTs_t *pdel_ts;
|
|
|
|
ENTER();
|
|
|
|
send_wmm_event = MFALSE;
|
|
if (resp_len < (int)sizeof(ptlv_hdr->header)) {
|
|
PRINTM(MINFO,
|
|
"WMM: WMM_GET_STATUS err: cmdresp low length received: %d\n",
|
|
resp_len);
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
PRINTM(MINFO, "WMM: WMM_GET_STATUS cmdresp received: %d\n", resp_len);
|
|
HEXDUMP("CMD_RESP: WMM_GET_STATUS", pcurrent, resp_len);
|
|
|
|
while (resp_len >= (int)sizeof(ptlv_hdr->header)) {
|
|
ptlv_hdr = (MrvlIEtypes_Data_t *)pcurrent;
|
|
tlv_len = wlan_le16_to_cpu(ptlv_hdr->header.len);
|
|
if ((int)(tlv_len + sizeof(ptlv_hdr->header)) > resp_len) {
|
|
PRINTM(MERROR,
|
|
"WMM get status: Error in processing TLV buffer\n");
|
|
resp_len = 0;
|
|
continue;
|
|
}
|
|
|
|
switch (wlan_le16_to_cpu(ptlv_hdr->header.type)) {
|
|
case TLV_TYPE_WMMQSTATUS:
|
|
ptlv_wmm_q_status =
|
|
(MrvlIEtypes_WmmQueueStatus_t *)ptlv_hdr;
|
|
PRINTM(MEVENT, "WMM_STATUS: QSTATUS TLV: %d\n",
|
|
ptlv_wmm_q_status->queue_index);
|
|
|
|
PRINTM(MINFO,
|
|
"CMD_RESP: WMM_GET_STATUS: QSTATUS TLV: %d, %d, %d\n",
|
|
ptlv_wmm_q_status->queue_index,
|
|
ptlv_wmm_q_status->flow_required,
|
|
ptlv_wmm_q_status->disabled);
|
|
|
|
/* Pick the minimum among these to avoid array out of
|
|
* bounds */
|
|
ptlv_wmm_q_status->queue_index = MIN(
|
|
ptlv_wmm_q_status->queue_index, MAX_AC_QUEUES);
|
|
|
|
pac_status =
|
|
&priv->wmm.ac_status[ptlv_wmm_q_status
|
|
->queue_index];
|
|
pac_status->disabled = ptlv_wmm_q_status->disabled;
|
|
pac_status->flow_required =
|
|
ptlv_wmm_q_status->flow_required;
|
|
pac_status->flow_created =
|
|
ptlv_wmm_q_status->flow_created;
|
|
break;
|
|
|
|
case TLV_TYPE_VENDOR_SPECIFIC_IE: /* WMM_IE */
|
|
/*
|
|
* Point the regular IEEE IE 2 bytes into the NXP IE
|
|
* and setup the IEEE IE type and length byte fields
|
|
*/
|
|
|
|
PRINTM(MEVENT, "WMM STATUS: WMM IE\n");
|
|
|
|
HEXDUMP("WMM: WMM TLV:", (t_u8 *)ptlv_hdr, tlv_len + 4);
|
|
|
|
pwmm_param_ie =
|
|
(IEEEtypes_WmmParameter_t *)(pcurrent + 2);
|
|
pwmm_param_ie->vend_hdr.len = (t_u8)tlv_len;
|
|
pwmm_param_ie->vend_hdr.element_id = WMM_IE;
|
|
|
|
PRINTM(MINFO,
|
|
"CMD_RESP: WMM_GET_STATUS: WMM Parameter Set: %d\n",
|
|
pwmm_param_ie->qos_info.para_set_count);
|
|
|
|
memcpy_ext(priv->adapter,
|
|
(t_u8 *)&priv->curr_bss_params.bss_descriptor
|
|
.wmm_ie,
|
|
pwmm_param_ie,
|
|
(pwmm_param_ie->vend_hdr.len + 2),
|
|
sizeof(IEEEtypes_WmmParameter_t));
|
|
send_wmm_event = MTRUE;
|
|
break;
|
|
|
|
case TLV_TYPE_IEEE_ACTION_FRAME:
|
|
PRINTM(MEVENT, "WMM_STATUS: IEEE Action Frame\n");
|
|
ptlv_action = (MrvlIETypes_ActionFrame_t *)pcurrent;
|
|
|
|
ptlv_action->actionFrame.wmmAc.tspecAct.category =
|
|
wlan_le32_to_cpu(ptlv_action->actionFrame.wmmAc
|
|
.tspecAct.category);
|
|
if (ptlv_action->actionFrame.wmmAc.tspecAct.category ==
|
|
IEEE_MGMT_ACTION_CATEGORY_WMM_TSPEC) {
|
|
ptlv_action->actionFrame.wmmAc.tspecAct.action =
|
|
wlan_le32_to_cpu(
|
|
ptlv_action->actionFrame.wmmAc
|
|
.tspecAct.action);
|
|
switch (ptlv_action->actionFrame.wmmAc.tspecAct
|
|
.action) {
|
|
case TSPEC_ACTION_CODE_ADDTS_RSP:
|
|
padd_ts_rsp = &ptlv_action->actionFrame
|
|
.wmmAc.addTsRsp;
|
|
wlan_send_wmmac_host_event(
|
|
priv, "ADDTS_RSP",
|
|
ptlv_action->srcAddr,
|
|
padd_ts_rsp->tspecIE.TspecBody
|
|
.TSInfo.TID,
|
|
padd_ts_rsp->tspecIE.TspecBody
|
|
.TSInfo.UserPri,
|
|
padd_ts_rsp->statusCode);
|
|
break;
|
|
|
|
case TSPEC_ACTION_CODE_DELTS:
|
|
pdel_ts = &ptlv_action->actionFrame
|
|
.wmmAc.delTs;
|
|
wlan_send_wmmac_host_event(
|
|
priv, "DELTS_RX",
|
|
ptlv_action->srcAddr,
|
|
pdel_ts->tspecIE.TspecBody
|
|
.TSInfo.TID,
|
|
pdel_ts->tspecIE.TspecBody
|
|
.TSInfo.UserPri,
|
|
pdel_ts->reasonCode);
|
|
break;
|
|
|
|
case TSPEC_ACTION_CODE_ADDTS_REQ:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
pcurrent += (tlv_len + sizeof(ptlv_hdr->header));
|
|
resp_len -= (tlv_len + sizeof(ptlv_hdr->header));
|
|
}
|
|
|
|
wlan_wmm_setup_queue_priorities(priv, pwmm_param_ie);
|
|
wlan_wmm_setup_ac_downgrade(priv);
|
|
|
|
if (send_wmm_event) {
|
|
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 NXP extended IEEE IE.
|
|
*
|
|
* @param priv Pointer to the mlan_private driver data struct
|
|
* @param ppassoc_buf Output parameter: Pointer to the TLV output buffer,
|
|
* modified on return to point after the appended WMM TLV
|
|
* @param pwmm_ie Pointer to the WMM 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 **ppassoc_buf,
|
|
IEEEtypes_WmmParameter_t *pwmm_ie)
|
|
{
|
|
MrvlIEtypes_WmmParamSet_t *pwmm_tlv;
|
|
t_u32 ret_len = 0;
|
|
|
|
ENTER();
|
|
|
|
/* Null checks */
|
|
if (!ppassoc_buf) {
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
if (!(*ppassoc_buf)) {
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
if (!pwmm_ie) {
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
PRINTM(MINFO, "WMM: process assoc req: bss->wmmIe=0x%x\n",
|
|
pwmm_ie->vend_hdr.element_id);
|
|
|
|
if (priv->wmm_required && pwmm_ie->vend_hdr.element_id == WMM_IE) {
|
|
pwmm_tlv = (MrvlIEtypes_WmmParamSet_t *)*ppassoc_buf;
|
|
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_ext(priv->adapter, pwmm_tlv->wmm_ie, &wmm_info_ie[2],
|
|
pwmm_tlv->header.len, pwmm_tlv->header.len);
|
|
if (pwmm_ie->qos_info.qos_uapsd)
|
|
memcpy_ext(priv->adapter,
|
|
(t_u8 *)(pwmm_tlv->wmm_ie +
|
|
pwmm_tlv->header.len -
|
|
sizeof(priv->wmm_qosinfo)),
|
|
&priv->wmm_qosinfo,
|
|
sizeof(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);
|
|
*ppassoc_buf += ret_len;
|
|
}
|
|
|
|
LEAVE();
|
|
return ret_len;
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
|
|
/**
|
|
* @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 Ptr to the mlan_private driver data struct
|
|
* @param pmbuf Ptr 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 ret_val = 0;
|
|
t_u32 out_ts_sec, out_ts_usec;
|
|
t_s32 queue_delay;
|
|
|
|
ENTER();
|
|
|
|
priv->adapter->callbacks.moal_get_system_time(
|
|
priv->adapter->pmoal_handle, &out_ts_sec, &out_ts_usec);
|
|
if (priv->adapter->tp_state_on) {
|
|
pmbuf->out_ts_sec = out_ts_sec;
|
|
pmbuf->out_ts_usec = out_ts_usec;
|
|
if (pmbuf->in_ts_sec)
|
|
priv->adapter->callbacks.moal_tp_accounting(
|
|
priv->adapter->pmoal_handle, pmbuf, 11);
|
|
}
|
|
queue_delay = (t_s32)(out_ts_sec - pmbuf->in_ts_sec) * 1000;
|
|
queue_delay += (t_s32)(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
|
|
*/
|
|
ret_val = (t_u8)(MIN(queue_delay, (t_s32)priv->wmm.drv_pkt_delay_max) >>
|
|
1);
|
|
|
|
PRINTM(MINFO, "WMM: Pkt Delay: %d ms, %d ms sent to FW\n", queue_delay,
|
|
ret_val);
|
|
|
|
LEAVE();
|
|
return ret_val;
|
|
}
|
|
|
|
/**
|
|
* @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 {
|
|
if (wlan_dequeue_tx_packet(pmadapter))
|
|
break;
|
|
#ifdef SDIO
|
|
if (IS_SD(pmadapter->card_type) &&
|
|
(pmadapter->ireg & UP_LD_CMD_PORT_HOST_INT_STATUS)) {
|
|
wlan_send_mp_aggr_buf(pmadapter);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef PCIE
|
|
if (IS_PCIE(pmadapter->card_type) &&
|
|
(pmadapter->ireg &
|
|
pmadapter->pcard_pcie->reg->host_intr_event_rdy))
|
|
break;
|
|
#endif
|
|
#ifdef USB
|
|
if (IS_USB(pmadapter->card_type) && pmadapter->event_received)
|
|
break;
|
|
#endif
|
|
/* Check if busy */
|
|
} while (!pmadapter->data_sent && !pmadapter->tx_lock_flag &&
|
|
!wlan_wmm_lists_empty(pmadapter));
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief select wmm queue
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param tid TID 0-7
|
|
*
|
|
* @return wmm_queue priority (0-3)
|
|
*/
|
|
t_u8 wlan_wmm_select_queue(mlan_private *pmpriv, t_u8 tid)
|
|
{
|
|
pmlan_adapter pmadapter = pmpriv->adapter;
|
|
t_u8 i;
|
|
mlan_wmm_ac_e ac_down =
|
|
pmpriv->wmm.ac_down_graded_vals[wlan_wmm_convert_tos_to_ac(
|
|
pmadapter, tid)];
|
|
|
|
ENTER();
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if (pmpriv->wmm.queue_priority[i] == ac_down) {
|
|
LEAVE();
|
|
return i;
|
|
}
|
|
}
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Delete tx packets in RA list
|
|
*
|
|
* @param priv Pointer to the mlan_private driver data struct
|
|
* @param ra_list_head ra list header
|
|
* @param tid tid
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static INLINE t_u8 wlan_del_tx_pkts_in_ralist(pmlan_private priv,
|
|
mlan_list_head *ra_list_head,
|
|
int tid)
|
|
{
|
|
raListTbl *ra_list = MNULL;
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
pmlan_buffer pmbuf = MNULL;
|
|
t_u8 ret = MFALSE;
|
|
ENTER();
|
|
ra_list = (raListTbl *)util_peek_list(priv->adapter->pmoal_handle,
|
|
ra_list_head, MNULL, MNULL);
|
|
while (ra_list && ra_list != (raListTbl *)ra_list_head) {
|
|
if (ra_list->total_pkts &&
|
|
(ra_list->tx_pause ||
|
|
(ra_list->total_pkts > RX_LOW_THRESHOLD))) {
|
|
pmbuf = (pmlan_buffer)util_dequeue_list(
|
|
pmadapter->pmoal_handle, &ra_list->buf_head,
|
|
MNULL, MNULL);
|
|
if (pmbuf) {
|
|
PRINTM(MDATA,
|
|
"Drop pkts: tid=%d tx_pause=%d pkts=%d " MACSTR
|
|
"\n",
|
|
tid, ra_list->tx_pause,
|
|
ra_list->total_pkts,
|
|
MAC2STR(ra_list->ra));
|
|
wlan_write_data_complete(pmadapter, pmbuf,
|
|
MLAN_STATUS_FAILURE);
|
|
priv->wmm.pkts_queued[tid]--;
|
|
priv->num_drop_pkts++;
|
|
ra_list->total_pkts--;
|
|
if (ra_list->tx_pause)
|
|
priv->wmm.pkts_paused[tid]--;
|
|
else
|
|
util_scalar_decrement(
|
|
pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued,
|
|
MNULL, MNULL);
|
|
ret = MTRUE;
|
|
break;
|
|
}
|
|
}
|
|
ra_list = ra_list->pnext;
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Drop tx pkts
|
|
*
|
|
* @param priv Pointer to the mlan_private driver data struct
|
|
*
|
|
* @return N/A
|
|
*/
|
|
t_void wlan_drop_tx_pkts(pmlan_private priv)
|
|
{
|
|
int j;
|
|
static int i;
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
for (j = 0; j < MAX_NUM_TID; j++, i++) {
|
|
if (i == MAX_NUM_TID)
|
|
i = 0;
|
|
if (wlan_del_tx_pkts_in_ralist(
|
|
priv, &priv->wmm.tid_tbl_ptr[i].ra_list, i)) {
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Remove peer ralist
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param mac peer mac address
|
|
*
|
|
* @return N/A
|
|
*/
|
|
t_void wlan_wmm_delete_peer_ralist(pmlan_private priv, t_u8 *mac)
|
|
{
|
|
raListTbl *ra_list;
|
|
int i;
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
t_u32 pkt_cnt = 0;
|
|
t_u32 tx_pkts_queued = 0;
|
|
|
|
ENTER();
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
for (i = 0; i < MAX_NUM_TID; ++i) {
|
|
ra_list = wlan_wmm_get_ralist_node(priv, i, mac);
|
|
if (ra_list) {
|
|
PRINTM(MINFO, "delete sta ralist %p\n", ra_list);
|
|
priv->wmm.pkts_queued[i] -= ra_list->total_pkts;
|
|
if (ra_list->tx_pause)
|
|
priv->wmm.pkts_paused[i] -= ra_list->total_pkts;
|
|
else
|
|
pkt_cnt += ra_list->total_pkts;
|
|
wlan_wmm_del_pkts_in_ralist_node(priv, ra_list);
|
|
|
|
util_unlink_list(pmadapter->pmoal_handle,
|
|
&priv->wmm.tid_tbl_ptr[i].ra_list,
|
|
(pmlan_linked_list)ra_list, MNULL,
|
|
MNULL);
|
|
pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle,
|
|
(t_u8 *)ra_list);
|
|
if (priv->wmm.tid_tbl_ptr[i].ra_list_curr == ra_list)
|
|
priv->wmm.tid_tbl_ptr[i].ra_list_curr =
|
|
(raListTbl *)&priv->wmm.tid_tbl_ptr[i]
|
|
.ra_list;
|
|
}
|
|
}
|
|
if (pkt_cnt) {
|
|
tx_pkts_queued = util_scalar_read(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued,
|
|
MNULL, MNULL);
|
|
tx_pkts_queued -= pkt_cnt;
|
|
util_scalar_write(priv->adapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued, tx_pkts_queued,
|
|
MNULL, MNULL);
|
|
util_scalar_write(priv->adapter->pmoal_handle,
|
|
&priv->wmm.highest_queued_prio, HIGH_PRIO_TID,
|
|
MNULL, MNULL);
|
|
}
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
LEAVE();
|
|
}
|
|
|
|
#ifdef STA_SUPPORT
|
|
/**
|
|
* @brief Hold TDLS packets to tdls pending queue
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param mac station mac address
|
|
*
|
|
* @return N/A
|
|
*/
|
|
t_void wlan_hold_tdls_packets(pmlan_private priv, t_u8 *mac)
|
|
{
|
|
pmlan_buffer pmbuf;
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
raListTbl *ra_list = MNULL;
|
|
t_u8 i;
|
|
|
|
ENTER();
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
PRINTM(MDATA, "wlan_hold_tdls_packets: " MACSTR "\n", MAC2STR(mac));
|
|
for (i = 0; i < MAX_NUM_TID; ++i) {
|
|
ra_list = (raListTbl *)util_peek_list(
|
|
pmadapter->pmoal_handle,
|
|
&priv->wmm.tid_tbl_ptr[i].ra_list, MNULL, MNULL);
|
|
if (ra_list) {
|
|
while ((pmbuf = wlan_find_tdls_packets(priv, ra_list,
|
|
mac))) {
|
|
util_unlink_list(pmadapter->pmoal_handle,
|
|
&ra_list->buf_head,
|
|
(pmlan_linked_list)pmbuf,
|
|
MNULL, MNULL);
|
|
ra_list->total_pkts--;
|
|
priv->wmm.pkts_queued[i]--;
|
|
util_scalar_decrement(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued,
|
|
MNULL, MNULL);
|
|
ra_list->packet_count--;
|
|
wlan_add_buf_tdls_txqueue(priv, pmbuf);
|
|
PRINTM(MDATA, "hold tdls packet=%p\n", pmbuf);
|
|
}
|
|
}
|
|
}
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief move TDLS packets back to ralist
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param mac TDLS peer mac address
|
|
* @param status tdlsStatus
|
|
*
|
|
* @return pmlan_buffer or MNULL
|
|
*/
|
|
t_void wlan_restore_tdls_packets(pmlan_private priv, t_u8 *mac,
|
|
tdlsStatus_e status)
|
|
{
|
|
pmlan_buffer pmbuf;
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
raListTbl *ra_list = MNULL;
|
|
t_u32 tid;
|
|
t_u32 tid_down;
|
|
|
|
ENTER();
|
|
PRINTM(MDATA, "wlan_restore_tdls_packets: " MACSTR " status=%d\n",
|
|
MAC2STR(mac), status);
|
|
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
|
|
while ((pmbuf = wlan_find_packets_tdls_txq(priv, mac))) {
|
|
util_unlink_list(pmadapter->pmoal_handle,
|
|
&priv->tdls_pending_txq,
|
|
(pmlan_linked_list)pmbuf, MNULL, MNULL);
|
|
tid = pmbuf->priority;
|
|
tid_down = wlan_wmm_downgrade_tid(priv, tid);
|
|
if (status == TDLS_SETUP_COMPLETE) {
|
|
ra_list = wlan_wmm_get_queue_raptr(priv, tid_down, mac);
|
|
pmbuf->flags |= MLAN_BUF_FLAG_TDLS;
|
|
} else {
|
|
ra_list = (raListTbl *)util_peek_list(
|
|
pmadapter->pmoal_handle,
|
|
&priv->wmm.tid_tbl_ptr[tid_down].ra_list, MNULL,
|
|
MNULL);
|
|
pmbuf->flags &= ~MLAN_BUF_FLAG_TDLS;
|
|
}
|
|
if (!ra_list) {
|
|
PRINTM_NETINTF(MWARN, priv);
|
|
PRINTM(MWARN,
|
|
"Drop packet %p, ra_list=%p media_connected=%d\n",
|
|
pmbuf, ra_list, priv->media_connected);
|
|
wlan_write_data_complete(pmadapter, pmbuf,
|
|
MLAN_STATUS_FAILURE);
|
|
continue;
|
|
}
|
|
PRINTM_NETINTF(MDATA, priv);
|
|
PRINTM(MDATA,
|
|
"ADD TDLS pkt %p (priority=%d) back to ra_list %p\n",
|
|
pmbuf, pmbuf->priority, ra_list);
|
|
util_enqueue_list_tail(pmadapter->pmoal_handle,
|
|
&ra_list->buf_head,
|
|
(pmlan_linked_list)pmbuf, MNULL, MNULL);
|
|
ra_list->total_pkts++;
|
|
ra_list->packet_count++;
|
|
priv->wmm.pkts_queued[tid_down]++;
|
|
util_scalar_increment(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued, MNULL, MNULL);
|
|
util_scalar_conditional_write(
|
|
pmadapter->pmoal_handle, &priv->wmm.highest_queued_prio,
|
|
MLAN_SCALAR_COND_LESS_THAN, tos_to_tid_inv[tid_down],
|
|
tos_to_tid_inv[tid_down], MNULL, MNULL);
|
|
}
|
|
if (status != TDLS_SETUP_COMPLETE)
|
|
wlan_wmm_delete_tdls_ralist(priv, mac);
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @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
|
|
*/
|
|
mlan_status wlan_cmd_wmm_addts_req(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *cmd, 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->ie_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_ext(pmpriv->adapter, pcmd_addts->tspec_data, paddts->ie_data,
|
|
paddts->ie_data_len, WMM_TSPEC_SIZE);
|
|
|
|
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(pmlan_private pmpriv,
|
|
const HostCmd_DS_COMMAND *resp,
|
|
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 = wlan_le32_to_cpu(presp_addts->command_result);
|
|
paddts->dialog_tok = presp_addts->dialog_token;
|
|
paddts->status_code = (t_u32)presp_addts->ieee_status_code;
|
|
|
|
if (paddts->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->ie_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_ext(pmpriv->adapter, paddts->ie_data,
|
|
presp_addts->tspec_data, paddts->ie_data_len,
|
|
sizeof(paddts->ie_data));
|
|
} else {
|
|
paddts->ie_data_len = 0;
|
|
}
|
|
PRINTM(MINFO, "TSPEC: ADDTS ret = %d,%d sz=%d\n",
|
|
paddts->result, paddts->status_code,
|
|
paddts->ie_data_len);
|
|
|
|
HEXDUMP("TSPEC: ADDTS data", paddts->ie_data,
|
|
paddts->ie_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
|
|
*/
|
|
mlan_status wlan_cmd_wmm_delts_req(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *cmd, 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->ie_data_len + S_DS_GEN);
|
|
cmd->result = 0;
|
|
pcmd_delts->ieee_reason_code = (t_u8)pdelts->status_code;
|
|
memcpy_ext(pmpriv->adapter, pcmd_delts->tspec_data, pdelts->ie_data,
|
|
pdelts->ie_data_len, WMM_TSPEC_SIZE);
|
|
|
|
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(pmlan_private pmpriv,
|
|
const HostCmd_DS_COMMAND *resp,
|
|
mlan_ioctl_req *pioctl_buf)
|
|
{
|
|
mlan_ds_wmm_cfg *pwmm;
|
|
IEEEtypes_WMM_TSPEC_t *ptspec_ie;
|
|
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 =
|
|
wlan_le32_to_cpu(presp_delts->command_result);
|
|
|
|
PRINTM(MINFO, "TSPEC: DELTS result = %d\n",
|
|
presp_delts->command_result);
|
|
|
|
if (pwmm->param.delts.result == 0) {
|
|
ptspec_ie = (IEEEtypes_WMM_TSPEC_t *)
|
|
presp_delts->tspec_data;
|
|
wlan_send_wmmac_host_event(
|
|
pmpriv, "DELTS_TX", MNULL,
|
|
ptspec_ie->TspecBody.TSInfo.TID,
|
|
ptspec_ie->TspecBody.TSInfo.UserPri,
|
|
presp_delts->ieee_reason_code);
|
|
}
|
|
}
|
|
|
|
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
|
|
*/
|
|
mlan_status wlan_cmd_wmm_queue_stats(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *cmd, 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->select_is_userpri = 1;
|
|
pcmd_qstats->select_bin = pqstats->user_priority;
|
|
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(pmlan_private pmpriv,
|
|
const HostCmd_DS_COMMAND *resp,
|
|
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->user_priority = presp_qstats->select_bin;
|
|
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
|
|
*/
|
|
mlan_status wlan_cmd_wmm_ts_status(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *cmd, 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_ext(pmpriv->adapter, (t_void *)pcmd_ts_status,
|
|
(t_void *)pts_status, sizeof(HostCmd_DS_WMM_TS_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(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *resp,
|
|
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_ext(pmpriv->adapter, (t_void *)&pwmm->param.ts_status,
|
|
(t_void *)presp_ts_status,
|
|
sizeof(mlan_ds_wmm_ts_status),
|
|
sizeof(mlan_ds_wmm_ts_status));
|
|
}
|
|
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get WMM status
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success
|
|
*/
|
|
static mlan_status wlan_wmm_ioctl_enable(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_wmm_cfg *wmm = MNULL;
|
|
ENTER();
|
|
wmm = (mlan_ds_wmm_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_GET)
|
|
wmm->param.wmm_enable = (t_u32)pmpriv->wmm_required;
|
|
else
|
|
pmpriv->wmm_required = (t_u8)wmm->param.wmm_enable;
|
|
pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE;
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get WMM QoS configuration
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success
|
|
*/
|
|
static mlan_status wlan_wmm_ioctl_qos(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_wmm_cfg *wmm = MNULL;
|
|
|
|
ENTER();
|
|
|
|
wmm = (mlan_ds_wmm_cfg *)pioctl_req->pbuf;
|
|
|
|
if (pioctl_req->action == MLAN_ACT_GET)
|
|
wmm->param.qos_cfg = pmpriv->wmm_qosinfo;
|
|
else {
|
|
pmpriv->wmm_qosinfo = wmm->param.qos_cfg;
|
|
pmpriv->saved_wmm_qosinfo = wmm->param.qos_cfg;
|
|
}
|
|
|
|
pioctl_req->data_read_written = sizeof(t_u8) + MLAN_SUB_COMMAND_SIZE;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Request for add a TSPEC
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_wmm_ioctl_addts_req(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_wmm_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf;
|
|
|
|
/* Send request to firmware */
|
|
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_ADDTS_REQ, 0, 0,
|
|
(t_void *)pioctl_req,
|
|
(t_void *)&cfg->param.addts);
|
|
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Request for delete a TSPEC
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_wmm_ioctl_delts_req(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_wmm_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf;
|
|
|
|
/* Send request to firmware */
|
|
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_DELTS_REQ, 0, 0,
|
|
(t_void *)pioctl_req,
|
|
(t_void *)&cfg->param.delts);
|
|
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief To get and start/stop queue stats on a WMM AC
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_wmm_ioctl_queue_stats(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_wmm_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf;
|
|
|
|
/* Send request to firmware */
|
|
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_QUEUE_STATS, 0, 0,
|
|
(t_void *)pioctl_req,
|
|
(t_void *)&cfg->param.q_stats);
|
|
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the status of the WMM AC queues
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success
|
|
*/
|
|
static mlan_status wlan_wmm_ioctl_queue_status(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_wmm_cfg *cfg = MNULL;
|
|
mlan_ds_wmm_queue_status *pqstatus = MNULL;
|
|
WmmAcStatus_t *pac_status = MNULL;
|
|
mlan_wmm_ac_e ac_idx;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf;
|
|
pqstatus = (mlan_ds_wmm_queue_status *)&cfg->param.q_status;
|
|
|
|
for (ac_idx = WMM_AC_BK; ac_idx <= WMM_AC_VO; ac_idx++) {
|
|
pac_status = &pmpriv->wmm.ac_status[ac_idx];
|
|
|
|
/* Firmware status */
|
|
pqstatus->ac_status[ac_idx].flow_required =
|
|
pac_status->flow_required;
|
|
pqstatus->ac_status[ac_idx].flow_created =
|
|
pac_status->flow_created;
|
|
pqstatus->ac_status[ac_idx].disabled = pac_status->disabled;
|
|
|
|
/* ACM bit reflected in firmware status (redundant) */
|
|
pqstatus->ac_status[ac_idx].wmm_acm = pac_status->flow_required;
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the status of the WMM Traffic Streams
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_wmm_ioctl_ts_status(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_wmm_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf;
|
|
|
|
/* Send request to firmware */
|
|
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_TS_STATUS, 0, 0,
|
|
(t_void *)pioctl_req,
|
|
(t_void *)&cfg->param.ts_status);
|
|
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
|
|
/**
|
|
* @brief This function prepares the command of WMM_PARAM_CONFIG
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param cmd A pointer to HostCmd_DS_COMMAND structure
|
|
* @param cmd_action cmd action.
|
|
* @param pdata_buf A pointer to data buffer
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_cmd_wmm_param_config(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *cmd, t_u8 cmd_action,
|
|
t_void *pdata_buf)
|
|
{
|
|
wmm_ac_parameters_t *ac_params = (wmm_ac_parameters_t *)pdata_buf;
|
|
HostCmd_DS_WMM_PARAM_CONFIG *pcmd_cfg = &cmd->params.param_config;
|
|
t_u8 i = 0;
|
|
|
|
ENTER();
|
|
|
|
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_PARAM_CONFIG);
|
|
cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_WMM_PARAM_CONFIG) +
|
|
S_DS_GEN);
|
|
cmd->result = 0;
|
|
|
|
pcmd_cfg->action = cmd_action;
|
|
if (cmd_action == HostCmd_ACT_GEN_SET) {
|
|
memcpy_ext(pmpriv->adapter, pcmd_cfg->ac_params, ac_params,
|
|
sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES,
|
|
sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES);
|
|
for (i = 0; i < MAX_AC_QUEUES; i++) {
|
|
pcmd_cfg->ac_params[i].tx_op_limit = wlan_cpu_to_le16(
|
|
pcmd_cfg->ac_params[i].tx_op_limit);
|
|
}
|
|
}
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles the command response of WMM_PARAM_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_param_config(pmlan_private pmpriv,
|
|
const HostCmd_DS_COMMAND *resp,
|
|
mlan_ioctl_req *pioctl_buf)
|
|
{
|
|
mlan_ds_wmm_cfg *pwmm = MNULL;
|
|
HostCmd_DS_WMM_PARAM_CONFIG *pcfg =
|
|
(HostCmd_DS_WMM_PARAM_CONFIG *)&resp->params.param_config;
|
|
t_u8 i;
|
|
|
|
ENTER();
|
|
|
|
if (pioctl_buf) {
|
|
pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf;
|
|
for (i = 0; i < MAX_AC_QUEUES; i++) {
|
|
pcfg->ac_params[i].tx_op_limit = wlan_le16_to_cpu(
|
|
pcfg->ac_params[i].tx_op_limit);
|
|
}
|
|
memcpy_ext(pmpriv->adapter, pwmm->param.ac_params,
|
|
pcfg->ac_params,
|
|
sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES,
|
|
sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES);
|
|
}
|
|
|
|
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
|
|
*/
|
|
mlan_status wlan_cmd_wmm_queue_config(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *cmd,
|
|
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(pmlan_private pmpriv,
|
|
const HostCmd_DS_COMMAND *resp,
|
|
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 = wlan_le32_to_cpu(presp_qcfg->action);
|
|
pwmm->param.q_cfg.access_category =
|
|
wlan_le32_to_cpu(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 Set/Get a specified AC Queue's parameters
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_wmm_ioctl_queue_config(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_wmm_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf;
|
|
|
|
/* Send request to firmware */
|
|
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_QUEUE_CONFIG, 0, 0,
|
|
(t_void *)pioctl_req,
|
|
(t_void *)&cfg->param.q_cfg);
|
|
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief WMM configuration handler
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
mlan_status wlan_wmm_cfg_ioctl(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
mlan_ds_wmm_cfg *wmm = MNULL;
|
|
|
|
ENTER();
|
|
|
|
if (pioctl_req->buf_len < sizeof(mlan_ds_wmm_cfg)) {
|
|
PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n");
|
|
pioctl_req->data_read_written = 0;
|
|
pioctl_req->buf_len_needed = sizeof(mlan_ds_wmm_cfg);
|
|
pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER;
|
|
LEAVE();
|
|
return MLAN_STATUS_RESOURCE;
|
|
}
|
|
wmm = (mlan_ds_wmm_cfg *)pioctl_req->pbuf;
|
|
switch (wmm->sub_command) {
|
|
#ifdef STA_SUPPORT
|
|
case MLAN_OID_WMM_CFG_ENABLE:
|
|
status = wlan_wmm_ioctl_enable(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_WMM_CFG_QOS:
|
|
status = wlan_wmm_ioctl_qos(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_WMM_CFG_ADDTS:
|
|
status = wlan_wmm_ioctl_addts_req(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_WMM_CFG_DELTS:
|
|
status = wlan_wmm_ioctl_delts_req(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_WMM_CFG_QUEUE_STATS:
|
|
status = wlan_wmm_ioctl_queue_stats(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_WMM_CFG_QUEUE_STATUS:
|
|
status = wlan_wmm_ioctl_queue_status(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_WMM_CFG_TS_STATUS:
|
|
status = wlan_wmm_ioctl_ts_status(pmadapter, pioctl_req);
|
|
break;
|
|
#endif
|
|
case MLAN_OID_WMM_CFG_QUEUE_CONFIG:
|
|
status = wlan_wmm_ioctl_queue_config(pmadapter, pioctl_req);
|
|
break;
|
|
default:
|
|
pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID;
|
|
status = MLAN_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
LEAVE();
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @brief Get ralist info
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param buf A pointer to ralist_info structure
|
|
* @return number of ralist entry
|
|
*
|
|
*/
|
|
int wlan_get_ralist_info(mlan_private *priv, ralist_info *buf)
|
|
{
|
|
ralist_info *plist = buf;
|
|
mlan_list_head *ra_list_head = MNULL;
|
|
raListTbl *ra_list;
|
|
int i;
|
|
int count = 0;
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list;
|
|
ra_list =
|
|
(raListTbl *)util_peek_list(priv->adapter->pmoal_handle,
|
|
ra_list_head, MNULL, MNULL);
|
|
while (ra_list && ra_list != (raListTbl *)ra_list_head) {
|
|
if (ra_list->total_pkts) {
|
|
plist->total_pkts = ra_list->total_pkts;
|
|
plist->tid = i;
|
|
plist->tx_pause = ra_list->tx_pause;
|
|
memcpy_ext(priv->adapter, plist->ra,
|
|
ra_list->ra, MLAN_MAC_ADDR_LENGTH,
|
|
MLAN_MAC_ADDR_LENGTH);
|
|
plist++;
|
|
count++;
|
|
if (count >= MLAN_MAX_RALIST_NUM)
|
|
break;
|
|
}
|
|
ra_list = ra_list->pnext;
|
|
}
|
|
}
|
|
LEAVE();
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* @brief dump ralist info
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
*
|
|
* @return N/A
|
|
*
|
|
*/
|
|
void wlan_dump_ralist(mlan_private *priv)
|
|
{
|
|
mlan_list_head *ra_list_head = MNULL;
|
|
raListTbl *ra_list;
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
int i;
|
|
t_u32 tx_pkts_queued;
|
|
|
|
tx_pkts_queued =
|
|
util_scalar_read(pmadapter->pmoal_handle,
|
|
&priv->wmm.tx_pkts_queued, MNULL, MNULL);
|
|
PRINTM(MERROR, "bss_index = %d, tx_pkts_queued = %d\n", priv->bss_index,
|
|
tx_pkts_queued);
|
|
if (!tx_pkts_queued)
|
|
return;
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list;
|
|
ra_list =
|
|
(raListTbl *)util_peek_list(priv->adapter->pmoal_handle,
|
|
ra_list_head, MNULL, MNULL);
|
|
while (ra_list && ra_list != (raListTbl *)ra_list_head) {
|
|
if (ra_list->total_pkts) {
|
|
PRINTM(MERROR,
|
|
"ralist ra: %02x:%02x:%02x:%02x:%02x:%02x tid=%d pkts=%d pause=%d\n",
|
|
ra_list->ra[0], ra_list->ra[1],
|
|
ra_list->ra[2], ra_list->ra[3],
|
|
ra_list->ra[4], ra_list->ra[5], i,
|
|
ra_list->total_pkts, ra_list->tx_pause);
|
|
}
|
|
ra_list = ra_list->pnext;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief get tid down
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param tid tid
|
|
*
|
|
* @return tid_down
|
|
*
|
|
*/
|
|
int wlan_get_wmm_tid_down(mlan_private *priv, int tid)
|
|
{
|
|
return wlan_wmm_downgrade_tid(priv, tid);
|
|
}
|