blob: 9c2ad342caa4f63efb1e53c373ab4b2ed2e08529 [file] [log] [blame]
/*
* Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*============================================================================
@file wlan_hdd_wmm.c
This module (wlan_hdd_wmm.h interface + wlan_hdd_wmm.c implementation)
houses all the logic for WMM in HDD.
On the control path, it has the logic to setup QoS, modify QoS and delete
QoS (QoS here refers to a TSPEC). The setup QoS comes in two flavors: an
explicit application invoked and an internal HDD invoked. The implicit QoS
is for applications that do NOT call the custom QCT WLAN OIDs for QoS but
which DO mark their traffic for priortization. It also has logic to start,
update and stop the U-APSD trigger frame generation. It also has logic to
read WMM related config parameters from the registry.
On the data path, it has the logic to figure out the WMM AC of an egress
packet and when to signal TL to serve a particular AC queue. It also has the
logic to retrieve a packet based on WMM priority in response to a fetch from
TL.
The remaining functions are utility functions for information hiding.
============================================================================*/
/*---------------------------------------------------------------------------
Include files
-------------------------------------------------------------------------*/
#include <wlan_hdd_tx_rx.h>
#include <wlan_hdd_dp_utils.h>
#include <wlan_hdd_wmm.h>
#include <wlan_hdd_ether.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/semaphore.h>
#include <wlan_hdd_hostapd.h>
#include <wlan_hdd_softap_tx_rx.h>
#include <vos_sched.h>
#include "sme_Api.h"
#include "sapInternal.h"
// change logging behavior based upon debug flag
#ifdef HDD_WMM_DEBUG
#define WMM_TRACE_LEVEL_FATAL VOS_TRACE_LEVEL_FATAL
#define WMM_TRACE_LEVEL_ERROR VOS_TRACE_LEVEL_FATAL
#define WMM_TRACE_LEVEL_WARN VOS_TRACE_LEVEL_FATAL
#define WMM_TRACE_LEVEL_INFO VOS_TRACE_LEVEL_FATAL
#define WMM_TRACE_LEVEL_INFO_HIGH VOS_TRACE_LEVEL_FATAL
#define WMM_TRACE_LEVEL_INFO_LOW VOS_TRACE_LEVEL_FATAL
#else
#define WMM_TRACE_LEVEL_FATAL VOS_TRACE_LEVEL_FATAL
#define WMM_TRACE_LEVEL_ERROR VOS_TRACE_LEVEL_ERROR
#define WMM_TRACE_LEVEL_WARN VOS_TRACE_LEVEL_WARN
#define WMM_TRACE_LEVEL_INFO VOS_TRACE_LEVEL_INFO
#define WMM_TRACE_LEVEL_INFO_HIGH VOS_TRACE_LEVEL_INFO_HIGH
#define WMM_TRACE_LEVEL_INFO_LOW VOS_TRACE_LEVEL_INFO_LOW
#endif
// DHCP Port number
#define DHCP_SOURCE_PORT 0x4400
#define DHCP_DESTINATION_PORT 0x4300
#define HDD_WMM_UP_TO_AC_MAP_SIZE 8
#define IPv6_ADDR_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5],\
(a)[6], (a)[7], (a)[8], (a)[9], (a)[10], (a)[11], (a)[12], (a)[13],\
(a)[14], (a)[15]
#define IPv6_ADDR_STR "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"\
"%02x%02x:%02x%02x"
const v_U8_t hddWmmUpToAcMap[] = {
WLANTL_AC_BE,
WLANTL_AC_BK,
WLANTL_AC_BK,
WLANTL_AC_BE,
WLANTL_AC_VI,
WLANTL_AC_VI,
WLANTL_AC_VO,
WLANTL_AC_VO
};
//Linux based UP -> AC Mapping
const v_U8_t hddLinuxUpToAcMap[8] = {
HDD_LINUX_AC_BE,
HDD_LINUX_AC_BK,
HDD_LINUX_AC_BK,
HDD_LINUX_AC_BE,
HDD_LINUX_AC_VI,
HDD_LINUX_AC_VI,
HDD_LINUX_AC_VO,
HDD_LINUX_AC_VO
};
#ifndef WLAN_MDM_CODE_REDUCTION_OPT
/**
@brief hdd_wmm_enable_tl_uapsd() - function which decides whether and
how to update UAPSD parameters in TL
@param pQosContext : [in] the pointer the QoS instance control block
@return
None
*/
static void hdd_wmm_enable_tl_uapsd (hdd_wmm_qos_context_t* pQosContext)
{
hdd_adapter_t* pAdapter = pQosContext->pAdapter;
WLANTL_ACEnumType acType = pQosContext->acType;
hdd_wmm_ac_status_t *pAc = NULL;
VOS_STATUS status;
v_U32_t service_interval;
v_U32_t suspension_interval;
sme_QosWmmDirType direction;
v_BOOL_t psb;
if (acType >= WLANTL_MAX_AC)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid AC: %d", __func__, acType);
return;
}
pAc = &pAdapter->hddWmmStatus.wmmAcStatus[acType];
// The TSPEC must be valid
if (pAc->wmmAcTspecValid == VOS_FALSE)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invoked with invalid TSPEC",
__func__);
return;
}
// determine the service interval
if (pAc->wmmAcTspecInfo.min_service_interval)
{
service_interval = pAc->wmmAcTspecInfo.min_service_interval;
}
else if (pAc->wmmAcTspecInfo.max_service_interval)
{
service_interval = pAc->wmmAcTspecInfo.max_service_interval;
}
else
{
// no service interval is present in the TSPEC
// this is OK, there just won't be U-APSD
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: No service interval supplied",
__func__);
service_interval = 0;
}
// determine the suspension interval & direction
suspension_interval = pAc->wmmAcTspecInfo.suspension_interval;
direction = pAc->wmmAcTspecInfo.ts_info.direction;
psb = pAc->wmmAcTspecInfo.ts_info.psb;
// if we have previously enabled U-APSD, have any params changed?
if ((pAc->wmmAcUapsdInfoValid) &&
(pAc->wmmAcUapsdServiceInterval == service_interval) &&
(pAc->wmmAcUapsdSuspensionInterval == suspension_interval) &&
(pAc->wmmAcUapsdDirection == direction) &&
(pAc->wmmAcIsUapsdEnabled == psb))
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: No change in U-APSD parameters",
__func__);
return;
}
// are we in the appropriate power save modes?
if (!sme_IsPowerSaveEnabled(WLAN_HDD_GET_HAL_CTX(pAdapter), ePMC_BEACON_MODE_POWER_SAVE))
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: BMPS is not enabled",
__func__);
return;
}
if (!sme_IsPowerSaveEnabled(WLAN_HDD_GET_HAL_CTX(pAdapter), ePMC_UAPSD_MODE_POWER_SAVE))
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: U-APSD is not enabled",
__func__);
return;
}
// everything is in place to notify TL
status = WLANTL_EnableUAPSDForAC((WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
acType,
pAc->wmmAcTspecInfo.ts_info.tid,
pAc->wmmAcTspecInfo.ts_info.up,
service_interval,
suspension_interval,
direction);
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Failed to enable U-APSD for AC=%d",
__func__, acType );
return;
}
// stash away the parameters that were used
pAc->wmmAcUapsdInfoValid = VOS_TRUE;
pAc->wmmAcUapsdServiceInterval = service_interval;
pAc->wmmAcUapsdSuspensionInterval = suspension_interval;
pAc->wmmAcUapsdDirection = direction;
pAc->wmmAcIsUapsdEnabled = psb;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Enabled UAPSD in TL srv_int=%d "
"susp_int=%d dir=%d AC=%d",
__func__,
service_interval,
suspension_interval,
direction,
acType);
}
/**
@brief hdd_wmm_disable_tl_uapsd() - function which decides whether
to disable UAPSD parameters in TL
@param pQosContext : [in] the pointer the QoS instance control block
@return
None
*/
static void hdd_wmm_disable_tl_uapsd (hdd_wmm_qos_context_t* pQosContext)
{
hdd_adapter_t* pAdapter = pQosContext->pAdapter;
WLANTL_ACEnumType acType = pQosContext->acType;
hdd_wmm_ac_status_t *pAc = NULL;
VOS_STATUS status;
v_U32_t service_interval;
v_U32_t suspension_interval;
v_U8_t uapsd_mask;
v_U8_t ActiveTspec = INVALID_TSPEC;
if (acType >= WLANTL_MAX_AC)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid AC: %d", __func__, acType);
return;
}
pAc = &pAdapter->hddWmmStatus.wmmAcStatus[acType];
// have we previously enabled UAPSD?
if (pAc->wmmAcUapsdInfoValid == VOS_TRUE)
{
uapsd_mask = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->UapsdMask;
//Finding uapsd_mask as per AC
uapsd_mask = uapsd_mask & (1 << (WLANTL_AC_VO - acType));
sme_QosTspecActive((tpAniSirGlobal)WLAN_HDD_GET_HAL_CTX(pAdapter), acType,
pAdapter->sessionId, &ActiveTspec);
//Call WLANTL_EnableUAPSDForAC only when static uapsd mask is present and
// no active tspecs. TODO: Need to change naming convention as Enable
// UAPSD function is called in hdd_wmm_disable_tl_uapsd. Purpose of
// calling WLANTL_EnableUAPSDForAC is to update UAPSD intervals to fw
if(uapsd_mask && !ActiveTspec)
{
switch(acType)
{
case WLANTL_AC_VO:
service_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdVoSrvIntv;
suspension_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdVoSuspIntv;
break;
case WLANTL_AC_VI:
service_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdViSrvIntv;
suspension_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdViSuspIntv;
break;
case WLANTL_AC_BE:
service_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBeSrvIntv;
suspension_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBeSuspIntv;
break;
case WLANTL_AC_BK:
service_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBkSrvIntv;
suspension_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBkSuspIntv;
break;
default:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid AC %d", __func__, acType );
return;
}
status = WLANTL_EnableUAPSDForAC((WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
acType,
pAc->wmmAcTspecInfo.ts_info.tid,
pAc->wmmAcTspecInfo.ts_info.up,
service_interval,
suspension_interval,
pAc->wmmAcTspecInfo.ts_info.direction);
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Failed to update U-APSD params for AC=%d",
__func__, acType );
}
else
{
// TL no longer has valid UAPSD info
pAc->wmmAcUapsdInfoValid = VOS_FALSE;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Updated UAPSD params in TL for AC=%d",
__func__,
acType);
}
}
}
}
#endif
/**
@brief hdd_wmm_free_context() - function which frees a QoS context
@param pQosContext : [in] the pointer the QoS instance control block
@return
None
*/
static void hdd_wmm_free_context (hdd_wmm_qos_context_t* pQosContext)
{
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx = NULL;
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("pVosContext is NULL"));
return;
}
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("HddCtx is NULL"));
return;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered, context %pK",
__func__, pQosContext);
// take the wmmLock since we're manipulating the context list
mutex_lock(&pHddCtx->wmmLock);
if (unlikely((NULL == pQosContext) ||
(HDD_WMM_CTX_MAGIC != pQosContext->magic)))
{
// must have been freed in another thread
mutex_unlock(&pHddCtx->wmmLock);
return;
}
// make sure nobody thinks this is a valid context
pQosContext->magic = 0;
// unlink the context
list_del(&pQosContext->node);
// done manipulating the list
mutex_unlock(&pHddCtx->wmmLock);
// reclaim memory
kfree(pQosContext);
}
#ifndef WLAN_MDM_CODE_REDUCTION_OPT
/**
@brief hdd_wmm_notify_app() - function which notifies an application
changes in state of it flow
@param pQosContext : [in] the pointer the QoS instance control block
@return
None
*/
#define MAX_NOTIFY_LEN 50
static void hdd_wmm_notify_app (hdd_wmm_qos_context_t* pQosContext)
{
hdd_adapter_t* pAdapter;
union iwreq_data wrqu;
char buf[MAX_NOTIFY_LEN+1];
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx = NULL;
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("pVosContext is NULL"));
return;
}
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("HddCtx is NULL"));
return;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered, context %pK",
__func__, pQosContext);
mutex_lock(&pHddCtx->wmmLock);
if (unlikely((NULL == pQosContext) ||
(HDD_WMM_CTX_MAGIC != pQosContext->magic)))
{
mutex_unlock(&pHddCtx->wmmLock);
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid QoS Context",
__func__);
return;
}
// get pointer to the adapter
pAdapter = pQosContext->pAdapter;
mutex_unlock(&pHddCtx->wmmLock);
// create the event
memset(&wrqu, 0, sizeof(wrqu));
memset(buf, 0, sizeof(buf));
snprintf(buf, MAX_NOTIFY_LEN, "QCOM: TS change[%u: %u]",
(unsigned int)pQosContext->handle,
(unsigned int)pQosContext->lastStatus);
wrqu.data.pointer = buf;
wrqu.data.length = strlen(buf);
// send the event
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Sending [%s]", __func__, buf);
wireless_send_event(pAdapter->dev, IWEVCUSTOM, &wrqu, buf);
}
/**
@brief hdd_wmm_is_access_allowed() - function which determines if access
is allowed for the given AC. this is designed to be called during SME
callback processing since that is when access can be granted or removed
@param pAdapter : [in] pointer to adapter context
@param pAc : [in] pointer to the per-AC status
@return : VOS_TRUE - access is allowed
: VOS_FALSE - access is not allowed
None
*/
static v_BOOL_t hdd_wmm_is_access_allowed(hdd_adapter_t* pAdapter,
hdd_wmm_ac_status_t* pAc)
{
// if we don't want QoS or the AP doesn't support QoS
// or we don't want to do implicit QoS
// or if AP doesn't require admission for this AC
// then we have access
if (!hdd_wmm_is_active(pAdapter) ||
!(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->bImplicitQosEnabled ||
!pAc->wmmAcAccessRequired)
{
return VOS_TRUE;
}
// if implicit QoS has already completed, successfully or not,
// then access is allowed
if (pAc->wmmAcAccessGranted || pAc->wmmAcAccessFailed)
{
return VOS_TRUE;
}
// admission is required and implicit QoS hasn't completed
// however explicit QoS may have completed and we'll have
// a Tspec
// if we don't have a Tspec then access is not allowed
if (!pAc->wmmAcTspecValid)
{
return VOS_FALSE;
}
// we have a Tspec -- does it allow upstream or bidirectional traffic?
// if it only allows downstream traffic then access is not allowed
if (pAc->wmmAcTspecInfo.ts_info.direction == SME_QOS_WMM_TS_DIR_DOWNLINK)
{
return VOS_FALSE;
}
// we meet all of the criteria for access
return VOS_TRUE;
}
#ifdef FEATURE_WLAN_ESE
/**
@brief hdd_wmm_inactivity_timer_cb() - timer handler function which is
called for every inactivity interval per AC. This function gets the
current transmitted packets on the given AC, and checks if there where
any TX activity from the previous interval. If there was no traffic
then it would delete the TS that was negotiated on that AC.
@param pUserData : [in] pointer to pQosContext
@return : NONE
*/
void hdd_wmm_inactivity_timer_cb( v_PVOID_t pUserData )
{
hdd_wmm_qos_context_t* pQosContext = (hdd_wmm_qos_context_t*)pUserData;
hdd_adapter_t* pAdapter;
hdd_wmm_ac_status_t *pAc;
hdd_wlan_wmm_status_e status;
VOS_STATUS vos_status;
v_U32_t currentTrafficCnt = 0;
WLANTL_ACEnumType acType = 0;
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx = NULL;
ENTER();
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid VOS Context", __func__);
return;
}
pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
mutex_lock(&pHddCtx->wmmLock);
if (unlikely((NULL == pQosContext) ||
(HDD_WMM_CTX_MAGIC != pQosContext->magic)))
{
mutex_unlock(&pHddCtx->wmmLock);
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid QoS Context",
__func__);
return;
}
mutex_unlock(&pHddCtx->wmmLock);
acType = pQosContext->acType;
pAdapter = pQosContext->pAdapter;
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("invalid pAdapter: %pK"), pAdapter);
return;
}
pAc = &pAdapter->hddWmmStatus.wmmAcStatus[acType];
// Get the Tx stats for this AC.
currentTrafficCnt = pAdapter->hdd_stats.hddTxRxStats.txXmitClassifiedAC[pQosContext->acType];
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
FL("WMM inactivity Timer for AC=%d, currentCnt=%d, prevCnt=%d"),
acType, (int)currentTrafficCnt, (int)pAc->wmmPrevTrafficCnt);
if (pAc->wmmPrevTrafficCnt == currentTrafficCnt)
{
// If there is no traffic activity, delete the TSPEC for this AC
status = hdd_wmm_delts(pAdapter, pQosContext->handle);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
FL("Deleted TS on AC %d, due to inactivity with status = %d!!!"),
acType, status);
}
else
{
pAc->wmmPrevTrafficCnt = currentTrafficCnt;
if (pAc->wmmInactivityTimer.state == VOS_TIMER_STATE_STOPPED)
{
// Restart the timer
vos_status = vos_timer_start(&pAc->wmmInactivityTimer, pAc->wmmInactivityTime);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Restarting inactivity timer failed on AC %d"), acType);
}
}
else
{
VOS_ASSERT(vos_timer_getCurrentState(
&pAc->wmmInactivityTimer) == VOS_TIMER_STATE_STOPPED);
}
}
EXIT();
return;
}
/**
@brief hdd_wmm_enable_inactivity_timer() - function to enable the
traffic inactivity timer for the given AC, if the inactivity_interval
specified in the ADDTS parameters is non-zero
@param pQosContext : [in] pointer to pQosContext
@param inactivityTime: [in] value of the inactivity interval in millisecs
@return : VOS_STATUS_E_FAILURE
VOS_STATUS_SUCCESS
*/
VOS_STATUS hdd_wmm_enable_inactivity_timer(hdd_wmm_qos_context_t* pQosContext, v_U32_t inactivityTime)
{
VOS_STATUS vos_status = VOS_STATUS_E_FAILURE;
hdd_adapter_t* pAdapter = pQosContext->pAdapter;
WLANTL_ACEnumType acType = pQosContext->acType;
hdd_wmm_ac_status_t *pAc;
pAdapter = pQosContext->pAdapter;
pAc = &pAdapter->hddWmmStatus.wmmAcStatus[acType];
// If QoS-Tspec is successfully setup and if the inactivity timer is non-zero,
// a traffic inactivity timer needs to be started for the given AC
vos_status = vos_timer_init(
&pAc->wmmInactivityTimer,
VOS_TIMER_TYPE_SW,
hdd_wmm_inactivity_timer_cb,
(v_PVOID_t)pQosContext );
if ( !VOS_IS_STATUS_SUCCESS(vos_status))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Initializing inactivity timer failed on AC %d"), acType);
return vos_status;
}
// Start the inactivity timer
vos_status = vos_timer_start(
&pAc->wmmInactivityTimer,
inactivityTime);
if ( !VOS_IS_STATUS_SUCCESS(vos_status))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Starting inactivity timer failed on AC %d"), acType);
return vos_status;
}
pAc->wmmInactivityTime = inactivityTime;
// Initialize the current tx traffic count on this AC
pAc->wmmPrevTrafficCnt = pAdapter->hdd_stats.hddTxRxStats.txXmitClassifiedAC[pQosContext->acType];
return vos_status;
}
/**
@brief hdd_wmm_enable_inactivity_timer() - function to disable the
traffic inactivity timer for the given AC. This would be called when
deleting the TS.
@param pQosContext : [in] pointer to pQosContext
@return : VOS_STATUS_E_FAILURE
VOS_STATUS_SUCCESS
*/
VOS_STATUS hdd_wmm_disable_inactivity_timer(hdd_wmm_qos_context_t* pQosContext)
{
hdd_adapter_t* pAdapter = pQosContext->pAdapter;
WLANTL_ACEnumType acType = pQosContext->acType;
hdd_wmm_ac_status_t *pAc = &pAdapter->hddWmmStatus.wmmAcStatus[acType];
VOS_STATUS vos_status = VOS_STATUS_E_FAILURE;
// Clear the timer and the counter
pAc->wmmInactivityTime = 0;
pAc->wmmPrevTrafficCnt = 0;
vos_timer_stop(&pAc->wmmInactivityTimer);
vos_status = vos_timer_destroy(&pAc->wmmInactivityTimer);
return vos_status;
}
#endif // FEATURE_WLAN_ESE
/**
@brief hdd_wmm_sme_callback() - callback registered by HDD with SME for receiving
QoS notifications. Even though this function has a static scope it gets called
externally through some function pointer magic (so there is a need for
rigorous parameter checking)
@param hHal : [in] the HAL handle
@param HddCtx : [in] the HDD specified handle
@param pCurrentQosInfo : [in] the TSPEC params
@param SmeStatus : [in] the QoS related SME status
@return
eHAL_STATUS_SUCCESS if all good, eHAL_STATUS_FAILURE otherwise
*/
static eHalStatus hdd_wmm_sme_callback (tHalHandle hHal,
void * hddCtx,
sme_QosWmmTspecInfo* pCurrentQosInfo,
sme_QosStatusType smeStatus,
v_U32_t qosFlowId)
{
hdd_wmm_qos_context_t* pQosContext = hddCtx;
hdd_adapter_t* pAdapter;
WLANTL_ACEnumType acType;
hdd_wmm_ac_status_t *pAc;
VOS_STATUS status;
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx = NULL;
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("pVosContext is NULL"));
return eHAL_STATUS_FAILURE;
}
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("HddCtx is NULL"));
return eHAL_STATUS_FAILURE;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered, context %pK",
__func__, pQosContext);
mutex_lock(&pHddCtx->wmmLock);
if (unlikely((NULL == pQosContext) ||
(HDD_WMM_CTX_MAGIC != pQosContext->magic)))
{
mutex_unlock(&pHddCtx->wmmLock);
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid QoS Context",
__func__);
return eHAL_STATUS_FAILURE;
}
mutex_unlock(&pHddCtx->wmmLock);
pAdapter = pQosContext->pAdapter;
acType = pQosContext->acType;
pAc = &pAdapter->hddWmmStatus.wmmAcStatus[acType];
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: status %d flowid %d info %pK",
__func__, smeStatus, qosFlowId, pCurrentQosInfo);
switch (smeStatus)
{
case SME_QOS_STATUS_SETUP_SUCCESS_IND:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Setup is complete",
__func__);
// there will always be a TSPEC returned with this status, even if
// a TSPEC is not exchanged OTA
if (pCurrentQosInfo)
{
pAc->wmmAcTspecValid = VOS_TRUE;
memcpy(&pAc->wmmAcTspecInfo,
pCurrentQosInfo,
sizeof(pAc->wmmAcTspecInfo));
}
if (HDD_WMM_HANDLE_IMPLICIT == pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Implicit Qos, notifying TL for TL AC %d",
__func__, acType);
// this was triggered by implicit QoS so we know packets are pending
// update state
pAc->wmmAcAccessAllowed = VOS_TRUE;
pAc->wmmAcAccessGranted = VOS_TRUE;
pAc->wmmAcAccessPending = VOS_FALSE;
// notify TL that packets are pending
status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
acType );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Failed to signal TL for AC=%d",
__func__, acType );
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS;
hdd_wmm_notify_app(pQosContext);
}
#ifdef FEATURE_WLAN_ESE
// Check if the inactivity interval is specified
if (pCurrentQosInfo && pCurrentQosInfo->inactivity_interval) {
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Inactivity timer value = %d for AC=%d",
__func__, pCurrentQosInfo->inactivity_interval, acType);
hdd_wmm_enable_inactivity_timer(pQosContext, pCurrentQosInfo->inactivity_interval);
}
#endif // FEATURE_WLAN_ESE
// notify TL to enable trigger frames if necessary
hdd_wmm_enable_tl_uapsd(pQosContext);
break;
case SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Setup is complete (U-APSD set previously)",
__func__);
if (HDD_WMM_HANDLE_IMPLICIT == pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Implicit Qos, notifying TL",
__func__);
// this was triggered by implicit QoS so we know packets are pending
// update state
pAc->wmmAcAccessAllowed = VOS_TRUE;
pAc->wmmAcAccessGranted = VOS_TRUE;
pAc->wmmAcAccessPending = VOS_FALSE;
// notify TL that packets are pending
status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
acType );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Failed to signal TL for AC=%d",
__func__, acType );
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_UAPSD_EXISTING;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_SETUP_FAILURE_RSP:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Setup failed",
__func__);
// QoS setup failed
if (HDD_WMM_HANDLE_IMPLICIT == pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Implicit Qos, notifying TL",
__func__);
// we note the failure, but we also mark access as allowed so that
// the packets will flow. Note that the MAC will "do the right thing"
pAc->wmmAcAccessPending = VOS_FALSE;
pAc->wmmAcAccessFailed = VOS_TRUE;
pAc->wmmAcAccessAllowed = VOS_TRUE;
// this was triggered by implicit QoS so we know packets are pending
status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
acType );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Failed to signal TL for AC=%d",
__func__, acType );
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_SETUP_FAILED;
hdd_wmm_notify_app(pQosContext);
}
#ifdef FEATURE_WLAN_ESE
hdd_wmm_disable_inactivity_timer(pQosContext);
#endif
/* Setting up QoS Failed, QoS context can be released.
* SME is releasing this flow information and if HDD doen't release this context,
* next time if application uses the same handle to set-up QoS, HDD (as it has
* QoS context for this handle) will issue Modify QoS request to SME but SME will
* reject as no it has no information for this flow.
*/
hdd_wmm_free_context(pQosContext);
break;
case SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Setup Invalid Params, notify TL",
__func__);
// QoS setup failed
if (HDD_WMM_HANDLE_IMPLICIT == pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Implicit Qos, notifying TL",
__func__);
// we note the failure, but we also mark access as allowed so that
// the packets will flow. Note that the MAC will "do the right thing"
pAc->wmmAcAccessPending = VOS_FALSE;
pAc->wmmAcAccessFailed = VOS_TRUE;
pAc->wmmAcAccessAllowed = VOS_TRUE;
// this was triggered by implicit QoS so we know packets are pending
status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
acType );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Failed to signal TL for AC=%d",
__func__, acType );
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP:
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Setup failed, not a QoS AP",
__func__);
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_SETUP_REQ_PENDING_RSP:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Setup pending",
__func__);
// not a callback status -- ignore if we get it
break;
case SME_QOS_STATUS_SETUP_MODIFIED_IND:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Setup modified",
__func__);
if (pCurrentQosInfo)
{
// update the TSPEC
pAc->wmmAcTspecValid = VOS_TRUE;
memcpy(&pAc->wmmAcTspecInfo,
pCurrentQosInfo,
sizeof(pAc->wmmAcTspecInfo));
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_MODIFIED;
hdd_wmm_notify_app(pQosContext);
}
// need to tell TL to update its UAPSD handling
hdd_wmm_enable_tl_uapsd(pQosContext);
}
break;
case SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
if (HDD_WMM_HANDLE_IMPLICIT == pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Implicit Qos, notifying TL",
__func__);
// this was triggered by implicit QoS so we know packets are pending
pAc->wmmAcAccessPending = VOS_FALSE;
pAc->wmmAcAccessGranted = VOS_TRUE;
pAc->wmmAcAccessAllowed = VOS_TRUE;
// notify TL that packets are pending
status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
acType );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Failed to signal TL for AC=%d",
__func__, acType );
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_NO_UAPSD;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING:
// nothing to do for now
break;
case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Setup successful but U-APSD failed",
__func__);
if (HDD_WMM_HANDLE_IMPLICIT == pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Implicit Qos, notifying TL",
__func__);
// QoS setup was successful but setting U=APSD failed
// Since the OTA part of the request was successful, we don't mark
// this as a failure.
// the packets will flow. Note that the MAC will "do the right thing"
pAc->wmmAcAccessGranted = VOS_TRUE;
pAc->wmmAcAccessAllowed = VOS_TRUE;
pAc->wmmAcAccessFailed = VOS_FALSE;
pAc->wmmAcAccessPending = VOS_FALSE;
// this was triggered by implicit QoS so we know packets are pending
status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
acType );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Failed to signal TL for AC=%d",
__func__, acType );
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_SETUP_UAPSD_SET_FAILED;
hdd_wmm_notify_app(pQosContext);
}
// Since U-APSD portion failed disabled trigger frame generation
hdd_wmm_disable_tl_uapsd(pQosContext);
break;
case SME_QOS_STATUS_RELEASE_SUCCESS_RSP:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Release is complete",
__func__);
if (pCurrentQosInfo)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: flows still active",
__func__);
// there is still at least one flow active for this AC
// so update the AC state
memcpy(&pAc->wmmAcTspecInfo,
pCurrentQosInfo,
sizeof(pAc->wmmAcTspecInfo));
// need to tell TL to update its UAPSD handling
hdd_wmm_enable_tl_uapsd(pQosContext);
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: last flow",
__func__);
// this is the last flow active for this AC so update the AC state
pAc->wmmAcTspecValid = VOS_FALSE;
// need to tell TL to update its UAPSD handling
hdd_wmm_disable_tl_uapsd(pQosContext);
}
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_RELEASE_SUCCESS;
hdd_wmm_notify_app(pQosContext);
}
#ifdef FEATURE_WLAN_ESE
hdd_wmm_disable_inactivity_timer(pQosContext);
#endif
// we are done with this flow
hdd_wmm_free_context(pQosContext);
break;
case SME_QOS_STATUS_RELEASE_FAILURE_RSP:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Release failure",
__func__);
// we don't need to update our state or TL since nothing has changed
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_RELEASE_FAILED;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_RELEASE_QOS_LOST_IND:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: QOS Lost indication received",
__func__);
// current TSPEC is no longer valid
pAc->wmmAcTspecValid = VOS_FALSE;
// need to tell TL to update its UAPSD handling
hdd_wmm_disable_tl_uapsd(pQosContext);
if (HDD_WMM_HANDLE_IMPLICIT == pQosContext->handle)
{
// we no longer have implicit access granted
pAc->wmmAcAccessGranted = VOS_FALSE;
pAc->wmmAcAccessFailed = VOS_FALSE;
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Explicit Qos, notifying userspace",
__func__);
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_LOST;
hdd_wmm_notify_app(pQosContext);
}
#ifdef FEATURE_WLAN_ESE
hdd_wmm_disable_inactivity_timer(pQosContext);
#endif
// we are done with this flow
hdd_wmm_free_context(pQosContext);
break;
case SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Release pending",
__func__);
// not a callback status -- ignore if we get it
break;
case SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Release Invalid Params",
__func__);
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Modification is complete, notify TL",
__func__);
// there will always be a TSPEC returned with this status, even if
// a TSPEC is not exchanged OTA
if (pCurrentQosInfo)
{
pAc->wmmAcTspecValid = VOS_TRUE;
memcpy(&pAc->wmmAcTspecInfo,
pCurrentQosInfo,
sizeof(pAc->wmmAcTspecInfo));
}
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS;
hdd_wmm_notify_app(pQosContext);
}
// notify TL to enable trigger frames if necessary
hdd_wmm_enable_tl_uapsd(pQosContext);
break;
case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY:
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_UAPSD_EXISTING;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP:
// the flow modification failed so we'll leave in place
// whatever existed beforehand
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_MODIFY_FAILED;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: modification pending",
__func__);
// not a callback status -- ignore if we get it
break;
case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
// the flow modification was successful but no QoS changes required
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_NO_UAPSD;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP:
// invalid params -- notify the application
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_MODIFY_FAILED_BAD_PARAM;
hdd_wmm_notify_app(pQosContext);
}
break;
case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_PENDING:
// nothing to do for now. when APSD is established we'll have work to do
break;
case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Modify successful but U-APSD failed",
__func__);
// QoS modification was successful but setting U=APSD failed.
// This will always be an explicit QoS instance, so all we can
// do is notify the application and let it clean up.
if (HDD_WMM_HANDLE_IMPLICIT != pQosContext->handle)
{
// this was triggered by an application
pQosContext->lastStatus = HDD_WLAN_WMM_STATUS_MODIFY_UAPSD_SET_FAILED;
hdd_wmm_notify_app(pQosContext);
}
// Since U-APSD portion failed disabled trigger frame generation
hdd_wmm_disable_tl_uapsd(pQosContext);
break;
case SME_QOS_STATUS_HANDING_OFF:
// no roaming so we won't see this
break;
case SME_QOS_STATUS_OUT_OF_APSD_POWER_MODE_IND:
// need to tell TL to stop trigger frame generation
hdd_wmm_disable_tl_uapsd(pQosContext);
break;
case SME_QOS_STATUS_INTO_APSD_POWER_MODE_IND:
// need to tell TL to start sending trigger frames again
hdd_wmm_enable_tl_uapsd(pQosContext);
break;
default:
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: unexpected SME Status=%d",
__func__, smeStatus );
VOS_ASSERT(0);
}
// our access to the particular access category may have changed.
// some of the implicit QoS cases above may have already set this
// prior to invoking TL (so that we will properly service the
// Tx queues) but let's consistently handle all cases here
pAc->wmmAcAccessAllowed = hdd_wmm_is_access_allowed(pAdapter, pAc);
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: complete, access for TL AC %d is%sallowed",
__func__,
acType,
pAc->wmmAcAccessAllowed ? " " : " not ");
return eHAL_STATUS_SUCCESS;
}
#endif
/**========================================================================
@brief hdd_wmmps_helper() - Function to set uapsd psb dynamically
@param pAdapter : [in] pointer to adapter structure
@param ptr : [in] pointer to command buffer
@return : Zero on success, appropriate error on failure.
=======================================================================*/
int hdd_wmmps_helper(hdd_adapter_t *pAdapter, tANI_U8 *ptr)
{
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: pAdapter is NULL", __func__);
return -EINVAL;
}
if (NULL == ptr)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: ptr is NULL", __func__);
return -EINVAL;
}
/* convert ASCII to integer */
pAdapter->configuredPsb = ptr[9] - '0';
pAdapter->psbChanged = HDD_PSB_CHANGED;
return 0;
}
/**============================================================================
@brief hdd_wmm_do_implicit_qos() - Function which will attempt to setup
QoS for any AC requiring it
@param work : [in] pointer to work structure
@return : void
===========================================================================*/
static void __hdd_wmm_do_implicit_qos(struct work_struct *work)
{
hdd_wmm_qos_context_t* pQosContext =
container_of(work, hdd_wmm_qos_context_t, wmmAcSetupImplicitQos);
hdd_adapter_t* pAdapter;
WLANTL_ACEnumType acType;
hdd_wmm_ac_status_t *pAc;
#ifndef WLAN_MDM_CODE_REDUCTION_OPT
VOS_STATUS status;
sme_QosStatusType smeStatus;
#endif
sme_QosWmmTspecInfo qosInfo;
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx = NULL;
int ret = 0;
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("pVosContext is NULL"));
return;
}
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
hddLog(LOGE, FL("HDD context is invalid"));
return;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered, context %pK",
__func__, pQosContext);
mutex_lock(&pHddCtx->wmmLock);
if (unlikely(HDD_WMM_CTX_MAGIC != pQosContext->magic))
{
mutex_unlock(&pHddCtx->wmmLock);
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid QoS Context",
__func__);
return;
}
mutex_unlock(&pHddCtx->wmmLock);
pAdapter = pQosContext->pAdapter;
acType = pQosContext->acType;
pAc = &pAdapter->hddWmmStatus.wmmAcStatus[acType];
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: pAdapter %pK acType %d",
__func__, pAdapter, acType);
if (!pAc->wmmAcAccessNeeded)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: AC %d doesn't need service",
__func__, acType);
pQosContext->magic = 0;
kfree(pQosContext);
return;
}
pAc->wmmAcAccessPending = VOS_TRUE;
pAc->wmmAcAccessNeeded = VOS_FALSE;
memset(&qosInfo, 0, sizeof(qosInfo));
qosInfo.ts_info.psb = pAdapter->configuredPsb;
switch (acType)
{
case WLANTL_AC_VO:
qosInfo.ts_info.up = SME_QOS_WMM_UP_VO;
/* Check if there is any valid configuration from framework */
if (HDD_PSB_CFG_INVALID == pAdapter->configuredPsb)
{
qosInfo.ts_info.psb = ((WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->UapsdMask &
SME_QOS_UAPSD_VO) ? 1 : 0;
}
qosInfo.ts_info.direction = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraDirAcVo;
qosInfo.ts_info.tid = 255;
qosInfo.mean_data_rate = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraMeanDataRateAcVo;
qosInfo.min_phy_rate = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraMinPhyRateAcVo;
qosInfo.min_service_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdVoSrvIntv;
qosInfo.nominal_msdu_size = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraNomMsduSizeAcVo;
qosInfo.surplus_bw_allowance = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraSbaAcVo;
qosInfo.suspension_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdVoSuspIntv;
break;
case WLANTL_AC_VI:
qosInfo.ts_info.up = SME_QOS_WMM_UP_VI;
/* Check if there is any valid configuration from framework */
if (HDD_PSB_CFG_INVALID == pAdapter->configuredPsb)
{
qosInfo.ts_info.psb = ((WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->UapsdMask &
SME_QOS_UAPSD_VI) ? 1 : 0;
}
qosInfo.ts_info.direction = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraDirAcVi;
qosInfo.ts_info.tid = 255;
qosInfo.mean_data_rate = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraMeanDataRateAcVi;
qosInfo.min_phy_rate = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraMinPhyRateAcVi;
qosInfo.min_service_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdViSrvIntv;
qosInfo.nominal_msdu_size = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraNomMsduSizeAcVi;
qosInfo.surplus_bw_allowance = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraSbaAcVi;
qosInfo.suspension_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdViSuspIntv;
break;
case WLANTL_AC_BE:
qosInfo.ts_info.up = SME_QOS_WMM_UP_BE;
/* Check if there is any valid configuration from framework */
if (HDD_PSB_CFG_INVALID == pAdapter->configuredPsb)
{
qosInfo.ts_info.psb = ((WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->UapsdMask &
SME_QOS_UAPSD_BE) ? 1 : 0;
}
qosInfo.ts_info.direction = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraDirAcBe;
qosInfo.ts_info.tid = 255;
qosInfo.mean_data_rate = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraMeanDataRateAcBe;
qosInfo.min_phy_rate = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraMinPhyRateAcBe;
qosInfo.min_service_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBeSrvIntv;
qosInfo.nominal_msdu_size = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraNomMsduSizeAcBe;
qosInfo.surplus_bw_allowance = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraSbaAcBe;
qosInfo.suspension_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBeSuspIntv;
break;
case WLANTL_AC_BK:
qosInfo.ts_info.up = SME_QOS_WMM_UP_BK;
/* Check if there is any valid configuration from framework */
if (HDD_PSB_CFG_INVALID == pAdapter->configuredPsb)
{
qosInfo.ts_info.psb = ((WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->UapsdMask &
SME_QOS_UAPSD_BK) ? 1 : 0;
}
qosInfo.ts_info.direction = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraDirAcBk;
qosInfo.ts_info.tid = 255;
qosInfo.mean_data_rate = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraMeanDataRateAcBk;
qosInfo.min_phy_rate = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraMinPhyRateAcBk;
qosInfo.min_service_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBkSrvIntv;
qosInfo.nominal_msdu_size = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraNomMsduSizeAcBk;
qosInfo.surplus_bw_allowance = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraSbaAcBk;
qosInfo.suspension_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBkSuspIntv;
break;
default:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid AC %d", __func__, acType );
return;
}
#ifdef FEATURE_WLAN_ESE
qosInfo.inactivity_interval = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraInactivityInterval;
#endif
qosInfo.ts_info.burst_size_defn = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->burstSizeDefinition;
switch ((WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->tsInfoAckPolicy)
{
case HDD_WLAN_WMM_TS_INFO_ACK_POLICY_NORMAL_ACK:
qosInfo.ts_info.ack_policy = SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK;
break;
case HDD_WLAN_WMM_TS_INFO_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK:
qosInfo.ts_info.ack_policy = SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK;
break;
default:
// unknown
qosInfo.ts_info.ack_policy = SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK;
}
if(qosInfo.ts_info.ack_policy == SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK)
{
if(!sme_QosIsTSInfoAckPolicyValid((tpAniSirGlobal)WLAN_HDD_GET_HAL_CTX(pAdapter), &qosInfo, pAdapter->sessionId))
{
qosInfo.ts_info.ack_policy = SME_QOS_WMM_TS_ACK_POLICY_NORMAL_ACK;
}
}
mutex_lock(&pHddCtx->wmmLock);
list_add(&pQosContext->node, &pAdapter->hddWmmStatus.wmmContextList);
mutex_unlock(&pHddCtx->wmmLock);
#ifndef WLAN_MDM_CODE_REDUCTION_OPT
smeStatus = sme_QosSetupReq(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
&qosInfo,
hdd_wmm_sme_callback,
pQosContext,
qosInfo.ts_info.up,
&pQosContext->qosFlowId);
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: sme_QosSetupReq returned %d flowid %d",
__func__, smeStatus, pQosContext->qosFlowId);
// need to check the return values and act appropriately
switch (smeStatus)
{
case SME_QOS_STATUS_SETUP_REQ_PENDING_RSP:
case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING:
// setup is pending, so no more work to do now.
// all further work will be done in hdd_wmm_sme_callback()
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Setup is pending, no further work",
__func__);
break;
case SME_QOS_STATUS_SETUP_FAILURE_RSP:
// we can't tell the difference between when a request fails because
// AP rejected it versus when SME encountered an internal error
// in either case SME won't ever reference this context so
// free the record
hdd_wmm_free_context(pQosContext);
// fall through and start packets flowing
case SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
// no ACM in effect, no need to setup U-APSD
case SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY:
// no ACM in effect, U-APSD is desired but was already setup
// for these cases everything is already setup so we can
// signal TL that it has work to do
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Setup is complete, notify TL",
__func__);
pAc->wmmAcAccessAllowed = VOS_TRUE;
pAc->wmmAcAccessGranted = VOS_TRUE;
pAc->wmmAcAccessPending = VOS_FALSE;
status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
acType );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Failed to signal TL for AC=%d",
__func__, acType );
}
break;
default:
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: unexpected SME Status=%d",
__func__, smeStatus );
VOS_ASSERT(0);
}
#endif
}
static void hdd_wmm_do_implicit_qos(struct work_struct *work)
{
vos_ssr_protect(__func__);
__hdd_wmm_do_implicit_qos( work );
vos_ssr_unprotect(__func__);
}
/**============================================================================
@brief hdd_wmm_init() - Function which will initialize the WMM configuation
and status to an initial state. The configuration can later be overwritten
via application APIs
@param pAdapter : [in] pointer to Adapter context
@return : VOS_STATUS_SUCCESS if successful
: other values if failure
===========================================================================*/
VOS_STATUS hdd_wmm_init ( hdd_adapter_t *pAdapter )
{
sme_QosWmmUpType* hddWmmDscpToUpMap = pAdapter->hddWmmDscpToUpMap;
v_U8_t dscp;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered", __func__);
// DSCP to User Priority Lookup Table
for (dscp = 0; dscp <= WLAN_MAX_DSCP; dscp++)
{
hddWmmDscpToUpMap[dscp] = SME_QOS_WMM_UP_BE;
}
hddWmmDscpToUpMap[8] = SME_QOS_WMM_UP_BK;
hddWmmDscpToUpMap[16] = SME_QOS_WMM_UP_RESV;
hddWmmDscpToUpMap[24] = SME_QOS_WMM_UP_EE;
hddWmmDscpToUpMap[32] = SME_QOS_WMM_UP_CL;
hddWmmDscpToUpMap[40] = SME_QOS_WMM_UP_VI;
hddWmmDscpToUpMap[48] = SME_QOS_WMM_UP_VO;
hddWmmDscpToUpMap[56] = SME_QOS_WMM_UP_NC;
return VOS_STATUS_SUCCESS;
}
/**============================================================================
@brief hdd_wmm_adapter_init() - Function which will initialize the WMM configuation
and status to an initial state. The configuration can later be overwritten
via application APIs
@param pAdapter : [in] pointer to Adapter context
@return : VOS_STATUS_SUCCESS if succssful
: other values if failure
===========================================================================*/
VOS_STATUS hdd_wmm_adapter_init( hdd_adapter_t *pAdapter )
{
hdd_wmm_ac_status_t *pAcStatus;
WLANTL_ACEnumType acType;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered", __func__);
pAdapter->hddWmmStatus.wmmQap = VOS_FALSE;
INIT_LIST_HEAD(&pAdapter->hddWmmStatus.wmmContextList);
for (acType = 0; acType < WLANTL_MAX_AC; acType++)
{
pAcStatus = &pAdapter->hddWmmStatus.wmmAcStatus[acType];
pAcStatus->wmmAcAccessRequired = VOS_FALSE;
pAcStatus->wmmAcAccessNeeded = VOS_FALSE;
pAcStatus->wmmAcAccessPending = VOS_FALSE;
pAcStatus->wmmAcAccessFailed = VOS_FALSE;
pAcStatus->wmmAcAccessGranted = VOS_FALSE;
pAcStatus->wmmAcAccessAllowed = VOS_FALSE;
pAcStatus->wmmAcTspecValid = VOS_FALSE;
pAcStatus->wmmAcUapsdInfoValid = VOS_FALSE;
}
// Invalid value(0xff) to indicate psb not configured through framework initially.
pAdapter->configuredPsb = HDD_PSB_CFG_INVALID;
return VOS_STATUS_SUCCESS;
}
/**============================================================================
@brief hdd_wmm_adapter_clear() - Function which will clear the WMM status
for all the ACs
@param pAdapter : [in] pointer to Adapter context
@return : VOS_STATUS_SUCCESS if succssful
: other values if failure
===========================================================================*/
VOS_STATUS hdd_wmm_adapter_clear( hdd_adapter_t *pAdapter )
{
hdd_wmm_ac_status_t *pAcStatus;
WLANTL_ACEnumType acType;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered", __func__);
for (acType = 0; acType < WLANTL_MAX_AC; acType++)
{
pAcStatus = &pAdapter->hddWmmStatus.wmmAcStatus[acType];
pAcStatus->wmmAcAccessRequired = VOS_FALSE;
pAcStatus->wmmAcAccessNeeded = VOS_FALSE;
pAcStatus->wmmAcAccessPending = VOS_FALSE;
pAcStatus->wmmAcAccessFailed = VOS_FALSE;
pAcStatus->wmmAcAccessGranted = VOS_FALSE;
pAcStatus->wmmAcAccessAllowed = VOS_FALSE;
pAcStatus->wmmAcTspecValid = VOS_FALSE;
pAcStatus->wmmAcUapsdInfoValid = VOS_FALSE;
}
return VOS_STATUS_SUCCESS;
}
/**============================================================================
@brief hdd_wmm_close() - Function which will perform any necessary work to
to clean up the WMM functionality prior to the kernel module unload
@param pAdapter : [in] pointer to adapter context
@return : VOS_STATUS_SUCCESS if succssful
: other values if failure
===========================================================================*/
VOS_STATUS hdd_wmm_adapter_close ( hdd_adapter_t* pAdapter )
{
hdd_wmm_qos_context_t* pQosContext;
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx = NULL;
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("pVosContext is NULL"));
return VOS_STATUS_E_FAILURE;
}
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("HddCtx is NULL"));
return VOS_STATUS_E_FAILURE;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered", __func__);
// free any context records that we still have linked
while (!list_empty(&pAdapter->hddWmmStatus.wmmContextList))
{
pQosContext = list_first_entry(&pAdapter->hddWmmStatus.wmmContextList,
hdd_wmm_qos_context_t, node);
#ifdef FEATURE_WLAN_ESE
hdd_wmm_disable_inactivity_timer(pQosContext);
#endif
mutex_lock(&pHddCtx->wmmLock);
if (pQosContext->handle == HDD_WMM_HANDLE_IMPLICIT
&& pQosContext->magic == HDD_WMM_CTX_MAGIC)
{
vos_flush_work(&pQosContext->wmmAcSetupImplicitQos);
}
mutex_unlock(&pHddCtx->wmmLock);
hdd_wmm_free_context(pQosContext);
}
return VOS_STATUS_SUCCESS;
}
/**============================================================================
@brief hdd_is_dhcp_packet() - Function which will check OS packet for
DHCP packet
@param skb : [in] pointer to OS packet (sk_buff)
@return : VOS_TRUE if the OS packet is DHCP packet
: otherwise VOS_FALSE
===========================================================================*/
v_BOOL_t hdd_is_dhcp_packet(struct sk_buff *skb)
{
if (*((u16*)((u8*)skb->data+34)) == DHCP_SOURCE_PORT ||
*((u16*)((u8*)skb->data+34)) == DHCP_DESTINATION_PORT)
return VOS_TRUE;
return VOS_FALSE;
}
/**============================================================================
@brief hdd_skb_is_eapol_or_wai_packet() - Function which will check OS packet
for Eapol/Wapi packet
@param skb : [in] pointer to OS packet (sk_buff)
@return : VOS_TRUE if the OS packet is an Eapol or a Wapi packet
: otherwise VOS_FALSE
===========================================================================*/
v_BOOL_t hdd_skb_is_eapol_or_wai_packet(struct sk_buff *skb)
{
if ((*((u16*)((u8*)skb->data+HDD_ETHERTYPE_802_1_X_FRAME_OFFSET))
== vos_cpu_to_be16(HDD_ETHERTYPE_802_1_X))
#ifdef FEATURE_WLAN_WAPI
|| (*((u16*)((u8*)skb->data+HDD_ETHERTYPE_802_1_X_FRAME_OFFSET))
== vos_cpu_to_be16(HDD_ETHERTYPE_WAI))
#endif
)
return VOS_TRUE;
return VOS_FALSE;
}
/**============================================================================
@brief hdd_log_ip_addr() - Function to log IP header src and dst address
@param skb : [in] pointer to OS packet (sk_buff)
@return : none
===========================================================================*/
void hdd_log_ip_addr(struct sk_buff *skb)
{
union generic_ethhdr *pHdr;
struct iphdr *pIpHdr;
unsigned char * pPkt;
char *buf;
pPkt = skb->data;
pHdr = (union generic_ethhdr *)pPkt;
if (pHdr->eth_II.h_proto == htons(ETH_P_IP))
{
pIpHdr = (struct iphdr *)&pPkt[sizeof(pHdr->eth_II)];
buf = (char *)&pIpHdr->saddr;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: src addr %d:%d:%d:%d", __func__,
buf[0], buf[1], buf[2], buf[3]);
buf = (char *)&pIpHdr->daddr;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: dst addr %d:%d:%d:%d", __func__,
buf[0], buf[1], buf[2], buf[3]);
}
else if (pHdr->eth_II.h_proto == htons(ETH_P_IPV6))
{
pIpHdr = (struct iphdr *)&pPkt[sizeof(pHdr->eth_8023)];
buf = (char *)&pIpHdr->saddr;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: src addr "IPv6_ADDR_STR, __func__, IPv6_ADDR_ARRAY(buf));
buf = (char *)&pIpHdr->daddr;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: dst addr "IPv6_ADDR_STR, __func__, IPv6_ADDR_ARRAY(buf));
}
else if ((ntohs(pHdr->eth_II.h_proto) < WLAN_MIN_PROTO) &&
(pHdr->eth_8023.h_snap.dsap == WLAN_SNAP_DSAP) &&
(pHdr->eth_8023.h_snap.ssap == WLAN_SNAP_SSAP) &&
(pHdr->eth_8023.h_snap.ctrl == WLAN_SNAP_CTRL))
{
if (pHdr->eth_8023.h_proto == htons(ETH_P_IP))
{
pIpHdr = (struct iphdr *)&pPkt[sizeof(pHdr->eth_8023)];
buf = (char *)&pIpHdr->saddr;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: src addr %d:%d:%d:%d", __func__,
buf[0], buf[1], buf[2], buf[3]);
buf = (char *)&pIpHdr->daddr;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: dst addr %d:%d:%d:%d", __func__,
buf[0], buf[1], buf[2], buf[3]);
}
else if (pHdr->eth_8023.h_proto == htons(ETH_P_IPV6))
{
pIpHdr = (struct iphdr *)&pPkt[sizeof(pHdr->eth_8023)];
buf = (char *)&pIpHdr->saddr;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: src addr "IPv6_ADDR_STR, __func__, IPv6_ADDR_ARRAY(buf));
buf = (char *)&pIpHdr->daddr;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: dst addr "IPv6_ADDR_STR, __func__, IPv6_ADDR_ARRAY(buf));
}
}
}
/**============================================================================
@brief hdd_get_arp_src_ip() - Function to get ARP src IP addr
@param skb : [in] pointer to OS packet (sk_buff)
@return : IP addr
===========================================================================*/
uint32_t hdd_get_arp_src_ip(struct sk_buff *skb)
{
struct arphdr *arp;
unsigned char *arp_ptr;
uint32_t src_ip;
#define ARP_HDR_SIZE ( sizeof(__be16)*3 + sizeof(unsigned char)*2 )
#define ARP_SENDER_HW_ADDRESS_SZ (6)
#define ARP_SENDER_IP_ADDRESS_SZ (4)
arp = (struct arphdr *)skb->data;
arp_ptr = (unsigned char *)arp + ARP_HDR_SIZE;
arp_ptr += ARP_SENDER_HW_ADDRESS_SZ;
memcpy(&src_ip, arp_ptr, ARP_SENDER_IP_ADDRESS_SZ);
src_ip=ntohl(src_ip);
return src_ip;
#undef ARP_HDR_SIZE
#undef ARP_SENDER_HW_ADDRESS_SZ
#undef ARP_SENDER_IP_ADDRESS_SZ
}
/**============================================================================
@brief hdd_wmm_classify_pkt() - Function which will classify an OS packet
into a WMM AC based on either 802.1Q or DSCP
@param pAdapter : [in] pointer to adapter context
@param skb : [in] pointer to OS packet (sk_buff)
@param pAcType : [out] pointer to WMM AC type of OS packet
@return : None
===========================================================================*/
v_VOID_t hdd_wmm_classify_pkt ( hdd_adapter_t* pAdapter,
struct sk_buff *skb,
WLANTL_ACEnumType* pAcType,
sme_QosWmmUpType *pUserPri)
{
unsigned char * pPkt;
union generic_ethhdr *pHdr;
struct iphdr *pIpHdr;
unsigned char tos;
unsigned char dscp;
sme_QosWmmUpType userPri;
WLANTL_ACEnumType acType;
// this code is executed for every packet therefore
// all debug code is kept conditional
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered", __func__);
#endif // HDD_WMM_DEBUG
pPkt = skb->data;
pHdr = (union generic_ethhdr *)pPkt;
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: proto/length is 0x%04x",
__func__, pHdr->eth_II.h_proto);
#endif // HDD_WMM_DEBUG
if (HDD_WMM_CLASSIFICATION_DSCP == (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->PktClassificationBasis)
{
if (pHdr->eth_II.h_proto == htons(ETH_P_IP))
{
// case 1: Ethernet II IP packet
pIpHdr = (struct iphdr *)&pPkt[sizeof(pHdr->eth_II)];
tos = pIpHdr->tos;
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Ethernet II IP Packet, tos is %d",
__func__, tos);
#endif // HDD_WMM_DEBUG
}
else if ((ntohs(pHdr->eth_II.h_proto) < WLAN_MIN_PROTO) &&
(pHdr->eth_8023.h_snap.dsap == WLAN_SNAP_DSAP) &&
(pHdr->eth_8023.h_snap.ssap == WLAN_SNAP_SSAP) &&
(pHdr->eth_8023.h_snap.ctrl == WLAN_SNAP_CTRL) &&
(pHdr->eth_8023.h_proto == htons(ETH_P_IP)))
{
// case 2: 802.3 LLC/SNAP IP packet
pIpHdr = (struct iphdr *)&pPkt[sizeof(pHdr->eth_8023)];
tos = pIpHdr->tos;
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: 802.3 LLC/SNAP IP Packet, tos is %d",
__func__, tos);
#endif // HDD_WMM_DEBUG
}
else if (pHdr->eth_II.h_proto == htons(ETH_P_8021Q))
{
// VLAN tagged
if (pHdr->eth_IIv.h_vlan_encapsulated_proto == htons(ETH_P_IP))
{
// case 3: Ethernet II vlan-tagged IP packet
pIpHdr = (struct iphdr *)&pPkt[sizeof(pHdr->eth_IIv)];
tos = pIpHdr->tos;
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Ethernet II VLAN tagged IP Packet, tos is %d",
__func__, tos);
#endif // HDD_WMM_DEBUG
}
else if ((ntohs(pHdr->eth_IIv.h_vlan_encapsulated_proto) < WLAN_MIN_PROTO) &&
(pHdr->eth_8023v.h_snap.dsap == WLAN_SNAP_DSAP) &&
(pHdr->eth_8023v.h_snap.ssap == WLAN_SNAP_SSAP) &&
(pHdr->eth_8023v.h_snap.ctrl == WLAN_SNAP_CTRL) &&
(pHdr->eth_8023v.h_proto == htons(ETH_P_IP)))
{
// case 4: 802.3 LLC/SNAP vlan-tagged IP packet
pIpHdr = (struct iphdr *)&pPkt[sizeof(pHdr->eth_8023v)];
tos = pIpHdr->tos;
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: 802.3 LLC/SNAP VLAN tagged IP Packet, tos is %d",
__func__, tos);
#endif // HDD_WMM_DEBUG
}
else
{
// default
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_WARN,
"%s: VLAN tagged Unhandled Protocol, using default tos",
__func__);
#endif // HDD_WMM_DEBUG
tos = 0;
}
}
else
{
v_BOOL_t toggleArpBDRates =
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->toggleArpBDRates;
// default
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_WARN,
"%s: Unhandled Protocol, using default tos",
__func__);
#endif // HDD_WMM_DEBUG
//Give the highest priority to 802.1x packet
if (pHdr->eth_II.h_proto == htons(HDD_ETHERTYPE_802_1_X))
tos = 0xC0;
else if (toggleArpBDRates &&
pHdr->eth_II.h_proto == htons(HDD_ETHERTYPE_ARP))
{
tos = TID3;
}
else
tos = 0;
}
dscp = (tos>>2) & 0x3f;
userPri = pAdapter->hddWmmDscpToUpMap[dscp];
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: tos is %d, dscp is %d, up is %d",
__func__, tos, dscp, userPri);
#endif // HDD_WMM_DEBUG
}
else if (HDD_WMM_CLASSIFICATION_802_1Q == (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->PktClassificationBasis)
{
if (pHdr->eth_IIv.h_vlan_proto == htons(ETH_P_8021Q))
{
// VLAN tagged
userPri = (ntohs(pHdr->eth_IIv.h_vlan_TCI)>>13) & 0x7;
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Tagged frame, UP is %d",
__func__, userPri);
#endif // HDD_WMM_DEBUG
}
else
{
// not VLAN tagged, use default
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_WARN,
"%s: Untagged frame, using default UP",
__func__);
#endif // HDD_WMM_DEBUG
//Give the highest priority to 802.1x packet
if (pHdr->eth_II.h_proto == htons(HDD_ETHERTYPE_802_1_X))
userPri = SME_QOS_WMM_UP_VO;
else
userPri = SME_QOS_WMM_UP_BE;
}
}
else
{
// default
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Unknown classification scheme, using default UP",
__func__);
#endif // HDD_WMM_DEBUG
userPri = SME_QOS_WMM_UP_BE;
}
acType = hddWmmUpToAcMap[userPri];
#ifdef HDD_WMM_DEBUG
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: UP is %d, AC is %d",
__func__, userPri, acType);
#endif // HDD_WMM_DEBUG
*pUserPri = userPri;
*pAcType = acType;
return;
}
/**============================================================================
@brief hdd_hostapd_select_quueue() - Function which will classify the packet
according to linux qdisc expectation.
@param dev : [in] pointer to net_device structure
@param skb : [in] pointer to os packet
@return : Qdisc queue index
===========================================================================*/
v_U16_t hdd_hostapd_select_queue(struct net_device * dev, struct sk_buff *skb
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0))
, void *accel_priv
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
, select_queue_fallback_t fallbac
#endif
)
{
WLANTL_ACEnumType ac;
sme_QosWmmUpType up = SME_QOS_WMM_UP_BE;
v_USHORT_t queueIndex;
v_MACADDR_t *pDestMacAddress = (v_MACADDR_t*)skb->data;
hdd_adapter_t *pAdapter = (hdd_adapter_t *)netdev_priv(dev);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
v_U8_t STAId;
v_CONTEXT_t pVosContext = ( WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
ptSapContext pSapCtx = NULL;
int status = 0;
status = wlan_hdd_validate_context(pHddCtx);
if (status !=0 )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("called during WDReset/unload"));
skb->priority = SME_QOS_WMM_UP_BE;
return HDD_LINUX_AC_BE;
}
pSapCtx = VOS_GET_SAP_CB(pVosContext);
if(pSapCtx == NULL){
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("psapCtx is NULL"));
STAId = HDD_WLAN_INVALID_STA_ID;
goto done;
}
/*Get the Station ID*/
STAId = hdd_sta_id_find_from_mac_addr(pAdapter, pDestMacAddress);
if (STAId == HDD_WLAN_INVALID_STA_ID || STAId >= WLAN_MAX_STA_COUNT) {
VOS_TRACE( VOS_MODULE_ID_HDD_SOFTAP, VOS_TRACE_LEVEL_INFO,
"%s: Failed to find right station", __func__);
goto done;
}
spin_lock_bh( &pSapCtx->staInfo_lock );
if (FALSE == vos_is_macaddr_equal(&pSapCtx->aStaInfo[STAId].macAddrSTA, pDestMacAddress))
{
VOS_TRACE( VOS_MODULE_ID_HDD_SOFTAP, VOS_TRACE_LEVEL_INFO,
"%s: Station MAC address does not matching", __func__);
STAId = HDD_WLAN_INVALID_STA_ID;
goto release_lock;
}
if (pSapCtx->aStaInfo[STAId].isUsed && pSapCtx->aStaInfo[STAId].isQosEnabled && (HDD_WMM_USER_MODE_NO_QOS != pHddCtx->cfg_ini->WmmMode))
{
/* Get the user priority from IP header & corresponding AC */
hdd_wmm_classify_pkt (pAdapter, skb, &ac, &up);
//If 3/4th of Tx queue is used then place the DHCP packet in VOICE AC queue
if (pSapCtx->aStaInfo[STAId].vosLowResource && hdd_is_dhcp_packet(skb))
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_WARN,
"%s: Making priority of DHCP packet as VOICE", __func__);
up = SME_QOS_WMM_UP_VO;
ac = hddWmmUpToAcMap[up];
}
}
release_lock:
spin_unlock_bh( &pSapCtx->staInfo_lock );
done:
skb->priority = up;
if(skb->priority < SME_QOS_WMM_UP_MAX)
queueIndex = hddLinuxUpToAcMap[skb->priority];
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: up=%d is going beyond max value", __func__, up);
queueIndex = hddLinuxUpToAcMap[SME_QOS_WMM_UP_BE];
}
return queueIndex;
}
/**============================================================================
@brief hdd_wmm_select_quueue() - Function which will classify the packet
according to linux qdisc expectation.
@param dev : [in] pointer to net_device structure
@param skb : [in] pointer to os packet
@return : Qdisc queue index
===========================================================================*/
v_U16_t hdd_wmm_select_queue(struct net_device * dev, struct sk_buff *skb)
{
WLANTL_ACEnumType ac;
sme_QosWmmUpType up = SME_QOS_WMM_UP_BE;
v_USHORT_t queueIndex;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int status = 0;
status = wlan_hdd_validate_context(pHddCtx);
if (status !=0) {
skb->priority = SME_QOS_WMM_UP_BE;
return HDD_LINUX_AC_BE;
}
/*Get the Station ID*/
if (WLAN_HDD_IBSS == pAdapter->device_mode)
{
v_MACADDR_t *pDestMacAddress = (v_MACADDR_t*)skb->data;
v_U8_t STAId;
STAId = hdd_sta_id_find_from_mac_addr(pAdapter, pDestMacAddress);
if ((STAId == HDD_WLAN_INVALID_STA_ID) &&
!vos_is_macaddr_broadcast( pDestMacAddress ) &&
!vos_is_macaddr_group(pDestMacAddress))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Failed to find right station pDestMacAddress: "
MAC_ADDRESS_STR , __func__,
MAC_ADDR_ARRAY(pDestMacAddress->bytes));
goto done;
}
}
/* All traffic will get equal opportuniy to transmit data frames. */
/* Get the user priority from IP header & corresponding AC */
hdd_wmm_classify_pkt (pAdapter, skb, &ac, &up);
/* If 3/4th of BE AC Tx queue is full,
* then place the DHCP packet in VOICE AC queue.
* Doing this for IBSS alone, since for STA interface
* types, these packets will be queued to the new queue.
*/
if ((WLAN_HDD_IBSS == pAdapter->device_mode) &&
pAdapter->isVosLowResource && hdd_is_dhcp_packet(skb))
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_WARN,
"%s: BestEffort Tx Queue is 3/4th full"
" Make DHCP packet's pri as VO", __func__);
up = SME_QOS_WMM_UP_VO;
ac = hddWmmUpToAcMap[up];
}
done:
skb->priority = up;
if(skb->priority < SME_QOS_WMM_UP_MAX)
queueIndex = hddLinuxUpToAcMap[skb->priority];
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: up=%d is going beyond max value", __func__, up);
queueIndex = hddLinuxUpToAcMap[SME_QOS_WMM_UP_BE];
}
if ((WLAN_HDD_IBSS != pAdapter->device_mode) &&
(hdd_is_dhcp_packet(skb) ||
hdd_skb_is_eapol_or_wai_packet(skb)))
{
/* If the packet is a DHCP packet or a Eapol packet or
* a Wapi packet, then queue it to the new queue for
* STA interfaces alone.
*/
queueIndex = WLANTL_AC_HIGH_PRIO;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: up=%d QIndex:%d", __func__, up, queueIndex);
}
return queueIndex;
}
/**==========================================================================
@brief hdd_wmm_acquire_access_required() - Function which will determine
acquire admittance for a WMM AC is required or not based on psb configuration
done in framework
@param pAdapter : [in] pointer to adapter structure
@param acType : [in] WMM AC type of OS packet
@return : void
===========================================================================*/
void hdd_wmm_acquire_access_required(hdd_adapter_t *pAdapter,
WLANTL_ACEnumType acType)
{
/* Each bit in the LSB nibble indicates 1 AC.
* Clearing the particular bit in LSB nibble to indicate
* access required
*/
switch(acType)
{
case WLANTL_AC_BK:
pAdapter->psbChanged &= ~SME_QOS_UAPSD_CFG_BK_CHANGED_MASK; /* clear first bit */
break;
case WLANTL_AC_BE:
pAdapter->psbChanged &= ~SME_QOS_UAPSD_CFG_BE_CHANGED_MASK; /* clear second bit */
break;
case WLANTL_AC_VI:
pAdapter->psbChanged &= ~SME_QOS_UAPSD_CFG_VI_CHANGED_MASK; /* clear third bit */
break;
case WLANTL_AC_VO:
pAdapter->psbChanged &= ~SME_QOS_UAPSD_CFG_VO_CHANGED_MASK; /* clear fourth bit */
break;
default:
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Invalid AC Type", __func__);
break;
}
}
/**============================================================================
@brief hdd_wmm_acquire_access() - Function which will attempt to acquire
admittance for a WMM AC
@param pAdapter : [in] pointer to adapter context
@param acType : [in] WMM AC type of OS packet
@param pGranted : [out] pointer to boolean flag when indicates if access
has been granted or not
@return : VOS_STATUS_SUCCESS if succssful
: other values if failure
===========================================================================*/
VOS_STATUS hdd_wmm_acquire_access( hdd_adapter_t* pAdapter,
WLANTL_ACEnumType acType,
v_BOOL_t * pGranted )
{
hdd_wmm_qos_context_t *pQosContext;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered for AC %d", __func__, acType);
if (!hdd_wmm_is_active(pAdapter) || !(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->bImplicitQosEnabled)
{
// either we don't want QoS or the AP doesn't support QoS
// or we don't want to do implicit QoS
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: QoS not configured on both ends ", __func__);
pAdapter->hddWmmStatus.wmmAcStatus[acType].wmmAcAccessAllowed = VOS_TRUE;
*pGranted = VOS_TRUE;
return VOS_STATUS_SUCCESS;
}
// do we already have an implicit QoS request pending for this AC?
if ((pAdapter->hddWmmStatus.wmmAcStatus[acType].wmmAcAccessNeeded) ||
(pAdapter->hddWmmStatus.wmmAcStatus[acType].wmmAcAccessPending))
{
// request already pending so we need to wait for that response
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Implicit QoS for TL AC %d already scheduled",
__func__, acType);
*pGranted = VOS_FALSE;
return VOS_STATUS_SUCCESS;
}
// did we already fail to establish implicit QoS for this AC?
// (if so, access should have been granted when the failure was handled)
if (pAdapter->hddWmmStatus.wmmAcStatus[acType].wmmAcAccessFailed)
{
// request previously failed
// allow access, but we'll be downgraded
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Implicit QoS for TL AC %d previously failed",
__func__, acType);
pAdapter->hddWmmStatus.wmmAcStatus[acType].wmmAcAccessAllowed = VOS_TRUE;
*pGranted = VOS_TRUE;
return VOS_STATUS_SUCCESS;
}
// we need to establish implicit QoS
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Need to schedule implicit QoS for TL AC %d, pAdapter is %pK",
__func__, acType, pAdapter);
pAdapter->hddWmmStatus.wmmAcStatus[acType].wmmAcAccessNeeded = VOS_TRUE;
pQosContext = kmalloc(sizeof(*pQosContext), GFP_ATOMIC);
if (NULL == pQosContext)
{
// no memory for QoS context. Nothing we can do but let data flow
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Unable to allocate context", __func__);
pAdapter->hddWmmStatus.wmmAcStatus[acType].wmmAcAccessAllowed = VOS_TRUE;
*pGranted = VOS_TRUE;
return VOS_STATUS_SUCCESS;
}
pQosContext->acType = acType;
pQosContext->pAdapter = pAdapter;
pQosContext->qosFlowId = 0;
pQosContext->handle = HDD_WMM_HANDLE_IMPLICIT;
pQosContext->magic = HDD_WMM_CTX_MAGIC;
vos_init_work(&pQosContext->wmmAcSetupImplicitQos,
hdd_wmm_do_implicit_qos);
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Scheduling work for AC %d, context %pK",
__func__, acType, pQosContext);
schedule_work(&pQosContext->wmmAcSetupImplicitQos);
// caller will need to wait until the work takes place and
// TSPEC negotiation completes
*pGranted = VOS_FALSE;
return VOS_STATUS_SUCCESS;
}
/**============================================================================
@brief hdd_wmm_assoc() - Function which will handle the housekeeping
required by WMM when association takes place
@param pAdapter : [in] pointer to adapter context
@param pRoamInfo: [in] pointer to roam information
@param eBssType : [in] type of BSS
@return : VOS_STATUS_SUCCESS if succssful
: other values if failure
===========================================================================*/
VOS_STATUS hdd_wmm_assoc( hdd_adapter_t* pAdapter,
tCsrRoamInfo *pRoamInfo,
eCsrRoamBssType eBssType )
{
tANI_U8 uapsdMask;
VOS_STATUS status;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
// when we associate we need to notify TL if it needs to enable
// UAPSD for any access categories
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered", __func__);
if (pRoamInfo->fReassocReq)
{
// when we reassociate we should continue to use whatever
// parameters were previously established. if we are
// reassociating due to a U-APSD change for a particular
// Access Category, then the change will be communicated
// to HDD via the QoS callback associated with the given
// flow, and U-APSD parameters will be updated there
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Reassoc so no work, Exiting", __func__);
return VOS_STATUS_SUCCESS;
}
// get the negotiated UAPSD Mask
uapsdMask = pRoamInfo->u.pConnectedProfile->modifyProfileFields.uapsd_mask;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: U-APSD mask is 0x%02x", __func__, (int) uapsdMask);
if (uapsdMask & HDD_AC_VO)
{
status = WLANTL_EnableUAPSDForAC( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
WLANTL_AC_VO,
7,
7,
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdVoSrvIntv,
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdVoSuspIntv,
WLANTL_BI_DIR );
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( status ));
}
if (uapsdMask & HDD_AC_VI)
{
status = WLANTL_EnableUAPSDForAC( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
WLANTL_AC_VI,
5,
5,
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdViSrvIntv,
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdViSuspIntv,
WLANTL_BI_DIR );
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( status ));
}
if (uapsdMask & HDD_AC_BK)
{
status = WLANTL_EnableUAPSDForAC( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
WLANTL_AC_BK,
2,
2,
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBkSrvIntv,
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBkSuspIntv,
WLANTL_BI_DIR );
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( status ));
}
if (uapsdMask & HDD_AC_BE)
{
status = WLANTL_EnableUAPSDForAC( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId[0],
WLANTL_AC_BE,
3,
3,
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBeSrvIntv,
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBeSuspIntv,
WLANTL_BI_DIR );
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( status ));
}
status = sme_UpdateDSCPtoUPMapping(pHddCtx->hHal,
pAdapter->hddWmmDscpToUpMap, pAdapter->sessionId);
if (!VOS_IS_STATUS_SUCCESS( status ))
{
hdd_wmm_init( pAdapter );
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Exiting", __func__);
return VOS_STATUS_SUCCESS;
}
static const v_U8_t acmMaskBit[WLANTL_MAX_AC] =
{
0x4, /* WLANTL_AC_BK */
0x8, /* WLANTL_AC_BE */
0x2, /* WLANTL_AC_VI */
0x1 /* WLANTL_AC_VO */
};
/**============================================================================
@brief hdd_wmm_connect() - Function which will handle the housekeeping
required by WMM when a connection is established
@param pAdapter : [in] pointer to adapter context
@param pRoamInfo: [in] pointer to roam information
@param eBssType : [in] type of BSS
@return : VOS_STATUS_SUCCESS if succssful
: other values if failure
===========================================================================*/
VOS_STATUS hdd_wmm_connect( hdd_adapter_t* pAdapter,
tCsrRoamInfo *pRoamInfo,
eCsrRoamBssType eBssType )
{
int ac;
v_BOOL_t qap;
v_BOOL_t qosConnection;
v_U8_t acmMask;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered", __func__);
if ((eCSR_BSS_TYPE_INFRASTRUCTURE == eBssType) &&
pRoamInfo &&
pRoamInfo->u.pConnectedProfile)
{
qap = pRoamInfo->u.pConnectedProfile->qap;
qosConnection = pRoamInfo->u.pConnectedProfile->qosConnection;
acmMask = pRoamInfo->u.pConnectedProfile->acm_mask;
}
else
{
/* TODO: if a non-qos IBSS peer joins the group make qap and qosConnection false.
*/
qap = VOS_TRUE;
qosConnection = VOS_TRUE;
acmMask = 0x0;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: qap is %d, qosConnection is %d, acmMask is 0x%x",
__func__, qap, qosConnection, acmMask);
pAdapter->hddWmmStatus.wmmQap = qap;
pAdapter->hddWmmStatus.wmmQosConnection = qosConnection;
for (ac = 0; ac < WLANTL_MAX_AC; ac++)
{
if (qap &&
qosConnection &&
(acmMask & acmMaskBit[ac]))
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: ac %d on",
__func__, ac);
// admission is required
pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessRequired = VOS_TRUE;
//Mark wmmAcAccessAllowed as True if implicit Qos is disabled as there
//is no need to hold packets in queue during hdd_tx_fetch_packet_cbk
if (!(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->bImplicitQosEnabled)
pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed =
VOS_TRUE;
else
pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed =
VOS_FALSE;
pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessGranted = VOS_FALSE;
/* Making TSPEC invalid here so downgrading can be happen while roaming
* It is expected this will be SET in hdd_wmm_sme_callback,once sme is
* done with the AddTspec.Here we avoid 11r and ccx based association.
This change is done only when reassoc to different AP.
*/
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL( "fReassocReq = %d"
#if defined (FEATURE_WLAN_ESE)
"isESEAssoc = %d"
#endif
#if defined (WLAN_FEATURE_VOWIFI_11R)
"is11rAssoc = %d"
#endif
),
pRoamInfo->fReassocReq
#if defined (FEATURE_WLAN_ESE)
,pRoamInfo->isESEAssoc
#endif
#if defined (WLAN_FEATURE_VOWIFI_11R)
,pRoamInfo->is11rAssoc
#endif
);
if ( !pRoamInfo->fReassocReq
#if defined (WLAN_FEATURE_VOWIFI_11R)
&&
!pRoamInfo->is11rAssoc
#endif
#if defined (FEATURE_WLAN_ESE)
&&
!pRoamInfo->isESEAssoc
#endif
)
{
pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcTspecValid = VOS_FALSE;
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: ac %d off",
__func__, ac);
// admission is not required so access is allowed
pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessRequired = VOS_FALSE;
pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed = VOS_TRUE;
}
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Exiting", __func__);
return VOS_STATUS_SUCCESS;
}
/**============================================================================
@brief hdd_wmm_get_uapsd_mask() - Function which will calculate the
initial value of the UAPSD mask based upon the device configuration
@param pAdapter : [in] pointer to adapter context
@param pUapsdMask: [in] pointer to where the UAPSD Mask is to be stored
@return : VOS_STATUS_SUCCESS if succssful
: other values if failure
===========================================================================*/
VOS_STATUS hdd_wmm_get_uapsd_mask( hdd_adapter_t* pAdapter,
tANI_U8 *pUapsdMask )
{
tANI_U8 uapsdMask;
if (HDD_WMM_USER_MODE_NO_QOS == (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->WmmMode)
{
// no QOS then no UAPSD
uapsdMask = 0;
}
else
{
// start with the default mask
uapsdMask = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->UapsdMask;
// disable UAPSD for any ACs with a 0 Service Interval
if( (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdVoSrvIntv == 0 )
{
uapsdMask &= ~HDD_AC_VO;
}
if( (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdViSrvIntv == 0 )
{
uapsdMask &= ~HDD_AC_VI;
}
if( (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBkSrvIntv == 0 )
{
uapsdMask &= ~HDD_AC_BK;
}
if( (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->InfraUapsdBeSrvIntv == 0 )
{
uapsdMask &= ~HDD_AC_BE;
}
}
// return calculated mask
*pUapsdMask = uapsdMask;
return VOS_STATUS_SUCCESS;
}
/**============================================================================
@brief hdd_wmm_is_active() - Function which will determine if WMM is
active on the current connection
@param pAdapter : [in] pointer to adapter context
@return : VOS_TRUE if WMM is enabled
: VOS_FALSE if WMM is not enabled
===========================================================================*/
v_BOOL_t hdd_wmm_is_active( hdd_adapter_t* pAdapter )
{
if ((!pAdapter->hddWmmStatus.wmmQosConnection) ||
(!pAdapter->hddWmmStatus.wmmQap))
{
return VOS_FALSE;
}
else
{
return VOS_TRUE;
}
}
/**============================================================================
@brief hdd_wmm_addts() - Function which will add a traffic spec at the
request of an application
@param pAdapter : [in] pointer to adapter context
@param handle : [in] handle to uniquely identify a TS
@param pTspec : [in] pointer to the traffic spec
@return : HDD_WLAN_WMM_STATUS_*
===========================================================================*/
hdd_wlan_wmm_status_e hdd_wmm_addts( hdd_adapter_t* pAdapter,
v_U32_t handle,
sme_QosWmmTspecInfo* pTspec )
{
hdd_wmm_qos_context_t *pQosContext;
hdd_wlan_wmm_status_e status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS ;
#ifndef WLAN_MDM_CODE_REDUCTION_OPT
sme_QosStatusType smeStatus;
#endif
v_BOOL_t found = VOS_FALSE;
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx = NULL;
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("pVosContext is NULL"));
return HDD_WLAN_WMM_STATUS_SETUP_FAILED;
}
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("HddCtx is NULL"));
return HDD_WLAN_WMM_STATUS_SETUP_FAILED;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered with handle 0x%x", __func__, handle);
// see if a context already exists with the given handle
mutex_lock(&pHddCtx->wmmLock);
list_for_each_entry(pQosContext,
&pAdapter->hddWmmStatus.wmmContextList,
node)
{
if (pQosContext->handle == handle)
{
found = VOS_TRUE;
break;
}
}
mutex_unlock(&pHddCtx->wmmLock);
if (found)
{
// record with that handle already exists
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Record already exists with handle 0x%x",
__func__, handle);
/* Application is trying to modify some of the Tspec params. Allow it */
smeStatus = sme_QosModifyReq(WLAN_HDD_GET_HAL_CTX(pAdapter),
pTspec,
pQosContext->qosFlowId);
// need to check the return value and act appropriately
switch (smeStatus)
{
case SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP:
status = HDD_WLAN_WMM_STATUS_MODIFY_PENDING;
break;
case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
status = HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_NO_UAPSD;
break;
case SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY:
status = HDD_WLAN_WMM_STATUS_MODIFY_SUCCESS_NO_ACM_UAPSD_EXISTING;
break;
case SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP:
status = HDD_WLAN_WMM_STATUS_MODIFY_FAILED_BAD_PARAM;
break;
case SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP:
status = HDD_WLAN_WMM_STATUS_MODIFY_FAILED;
break;
case SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP:
status = HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM;
break;
default:
// we didn't get back one of the SME_QOS_STATUS_MODIFY_* status codes
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: unexpected SME Status=%d", __func__, smeStatus );
VOS_ASSERT(0);
return HDD_WLAN_WMM_STATUS_MODIFY_FAILED;
}
mutex_lock(&pHddCtx->wmmLock);
if (pQosContext->magic == HDD_WMM_CTX_MAGIC)
{
pQosContext->lastStatus = status;
}
mutex_unlock(&pHddCtx->wmmLock);
return status;
}
pQosContext = kmalloc(sizeof(*pQosContext), GFP_KERNEL);
if (NULL == pQosContext)
{
// no memory for QoS context. Nothing we can do
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: Unable to allocate QoS context", __func__);
return HDD_WLAN_WMM_STATUS_INTERNAL_FAILURE;
}
// we assume the tspec has already been validated by the caller
pQosContext->handle = handle;
if (pTspec->ts_info.up < HDD_WMM_UP_TO_AC_MAP_SIZE)
pQosContext->acType = hddWmmUpToAcMap[pTspec->ts_info.up];
else {
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: ts_info.up (%d) larger than max value (%d), "
"use default acType (%d)",
__func__, pTspec->ts_info.up,
HDD_WMM_UP_TO_AC_MAP_SIZE - 1, hddWmmUpToAcMap[0]);
pQosContext->acType = hddWmmUpToAcMap[0];
}
pQosContext->pAdapter = pAdapter;
pQosContext->qosFlowId = 0;
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: Setting up QoS, context %pK",
__func__, pQosContext);
mutex_lock(&pHddCtx->wmmLock);
pQosContext->magic = HDD_WMM_CTX_MAGIC;
list_add(&pQosContext->node, &pAdapter->hddWmmStatus.wmmContextList);
mutex_unlock(&pHddCtx->wmmLock);
#ifndef WLAN_MDM_CODE_REDUCTION_OPT
smeStatus = sme_QosSetupReq(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
pTspec,
hdd_wmm_sme_callback,
pQosContext,
pTspec->ts_info.up,
&pQosContext->qosFlowId);
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO,
"%s: sme_QosSetupReq returned %d flowid %d",
__func__, smeStatus, pQosContext->qosFlowId);
// need to check the return value and act appropriately
switch (smeStatus)
{
case SME_QOS_STATUS_SETUP_REQ_PENDING_RSP:
status = HDD_WLAN_WMM_STATUS_SETUP_PENDING;
break;
case SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP:
status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_NO_UAPSD;
break;
case SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY:
status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS_NO_ACM_UAPSD_EXISTING;
break;
case SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING:
status = HDD_WLAN_WMM_STATUS_SETUP_PENDING;
break;
case SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP:
hdd_wmm_free_context(pQosContext);
return HDD_WLAN_WMM_STATUS_SETUP_FAILED_BAD_PARAM;
case SME_QOS_STATUS_SETUP_FAILURE_RSP:
// we can't tell the difference between when a request fails because
// AP rejected it versus when SME encounterd an internal error
hdd_wmm_free_context(pQosContext);
return HDD_WLAN_WMM_STATUS_SETUP_FAILED;
case SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP:
hdd_wmm_free_context(pQosContext);
return HDD_WLAN_WMM_STATUS_SETUP_FAILED_NO_WMM;
default:
// we didn't get back one of the SME_QOS_STATUS_SETUP_* status codes
hdd_wmm_free_context(pQosContext);
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: unexpected SME Status=%d", __func__, smeStatus );
VOS_ASSERT(0);
return HDD_WLAN_WMM_STATUS_SETUP_FAILED;
}
#endif
// we were successful, save the status
mutex_lock(&pHddCtx->wmmLock);
if (pQosContext->magic == HDD_WMM_CTX_MAGIC)
{
pQosContext->lastStatus = status;
}
mutex_unlock(&pHddCtx->wmmLock);
return status;
}
/**============================================================================
@brief hdd_wmm_delts() - Function which will delete a traffic spec at the
request of an application
@param pAdapter : [in] pointer to adapter context
@param handle : [in] handle to uniquely identify a TS
@return : HDD_WLAN_WMM_STATUS_*
===========================================================================*/
hdd_wlan_wmm_status_e hdd_wmm_delts( hdd_adapter_t* pAdapter,
v_U32_t handle )
{
hdd_wmm_qos_context_t *pQosContext;
v_BOOL_t found = VOS_FALSE;
WLANTL_ACEnumType acType = 0;
v_U32_t qosFlowId = 0;
hdd_wlan_wmm_status_e status = HDD_WLAN_WMM_STATUS_SETUP_SUCCESS ;
#ifndef WLAN_MDM_CODE_REDUCTION_OPT
sme_QosStatusType smeStatus;
#endif
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx = NULL;
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("pVosContext is NULL"));
return HDD_WLAN_WMM_STATUS_RELEASE_FAILED;
}
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("HddCtx is NULL"));
return HDD_WLAN_WMM_STATUS_RELEASE_FAILED;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered with handle 0x%x", __func__, handle);
// locate the context with the given handle
mutex_lock(&pHddCtx->wmmLock);
list_for_each_entry(pQosContext,
&pAdapter->hddWmmStatus.wmmContextList,
node)
{
if (pQosContext->handle == handle)
{
found = VOS_TRUE;
acType = pQosContext->acType;
qosFlowId = pQosContext->qosFlowId;
break;
}
}
mutex_unlock(&pHddCtx->wmmLock);
if (VOS_FALSE == found)
{
// we didn't find the handle
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: handle 0x%x not found", __func__, handle);
return HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: found handle 0x%x, flow %d, AC %d, context %pK",
__func__, handle, qosFlowId, acType, pQosContext);
#ifndef WLAN_MDM_CODE_REDUCTION_OPT
smeStatus = sme_QosReleaseReq( WLAN_HDD_GET_HAL_CTX(pAdapter), qosFlowId );
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: SME flow %d released, SME status %d",
__func__, qosFlowId, smeStatus);
switch(smeStatus)
{
case SME_QOS_STATUS_RELEASE_SUCCESS_RSP:
// this flow is the only one on that AC, so go ahead and update
// our TSPEC state for the AC
pAdapter->hddWmmStatus.wmmAcStatus[acType].wmmAcTspecValid = VOS_FALSE;
// need to tell TL to stop trigger timer, etc
hdd_wmm_disable_tl_uapsd(pQosContext);
#ifdef FEATURE_WLAN_ESE
// disable the inactivity timer
hdd_wmm_disable_inactivity_timer(pQosContext);
#endif
// we are done with this context
hdd_wmm_free_context(pQosContext);
// SME must not fire any more callbacks for this flow since the context
// is no longer valid
return HDD_WLAN_WMM_STATUS_RELEASE_SUCCESS;
case SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP:
// do nothing as we will get a response from SME
status = HDD_WLAN_WMM_STATUS_RELEASE_PENDING;
break;
case SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP:
// nothing we can do with the existing flow except leave it
status = HDD_WLAN_WMM_STATUS_RELEASE_FAILED_BAD_PARAM;
break;
case SME_QOS_STATUS_RELEASE_FAILURE_RSP:
// nothing we can do with the existing flow except leave it
status = HDD_WLAN_WMM_STATUS_RELEASE_FAILED;
default:
// we didn't get back one of the SME_QOS_STATUS_RELEASE_* status codes
VOS_TRACE( VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
"%s: unexpected SME Status=%d", __func__, smeStatus );
VOS_ASSERT(0);
status = HDD_WLAN_WMM_STATUS_RELEASE_FAILED;
}
#endif
mutex_lock(&pHddCtx->wmmLock);
if (pQosContext->magic == HDD_WMM_CTX_MAGIC)
{
pQosContext->lastStatus = status;
}
mutex_unlock(&pHddCtx->wmmLock);
return status;
}
/**============================================================================
@brief hdd_wmm_checkts() - Function which will return the status of a traffic
spec at the request of an application
@param pAdapter : [in] pointer to adapter context
@param handle : [in] handle to uniquely identify a TS
@return : HDD_WLAN_WMM_STATUS_*
===========================================================================*/
hdd_wlan_wmm_status_e hdd_wmm_checkts( hdd_adapter_t* pAdapter,
v_U32_t handle )
{
hdd_wmm_qos_context_t *pQosContext;
hdd_wlan_wmm_status_e status = HDD_WLAN_WMM_STATUS_LOST;
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx = NULL;
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("pVosContext is NULL"));
return HDD_WLAN_WMM_STATUS_LOST;
}
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_ERROR,
FL("HddCtx is NULL"));
return HDD_WLAN_WMM_STATUS_LOST;
}
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: Entered with handle 0x%x", __func__, handle);
// locate the context with the given handle
mutex_lock(&pHddCtx->wmmLock);
list_for_each_entry(pQosContext,
&pAdapter->hddWmmStatus.wmmContextList,
node)
{
if (pQosContext->handle == handle)
{
VOS_TRACE(VOS_MODULE_ID_HDD, WMM_TRACE_LEVEL_INFO_LOW,
"%s: found handle 0x%x, context %pK",
__func__, handle, pQosContext);
status = pQosContext->lastStatus;
break;
}
}
mutex_unlock(&pHddCtx->wmmLock);
return status;
}