blob: ec8b742263c859eee5fb216906c856a05d646b20 [file] [log] [blame]
/*
* Copyright (c) 2012-2015 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.
*/
#include "sme_api.h"
#include "sms_debug.h"
#include "csr_inside_api.h"
#include "sme_inside.h"
#include "p2p_api.h"
#include "lim_api.h"
#include "cfg_api.h"
#include "wma.h"
/*------------------------------------------------------------------
*
* handle SME remain on channel request.
*
*------------------------------------------------------------------*/
CDF_STATUS p2p_process_remain_on_channel_cmd(tpAniSirGlobal pMac,
tSmeCmd *p2pRemainonChn)
{
CDF_STATUS status = CDF_STATUS_E_FAILURE;
tSirRemainOnChnReq *pMsg;
uint32_t len;
tCsrRoamSession *pSession =
CSR_GET_SESSION(pMac, p2pRemainonChn->sessionId);
if (!pSession) {
sms_log(pMac, LOGE, FL(" session %d not found "),
p2pRemainonChn->sessionId);
goto error;
}
if (!pSession->sessionActive) {
sms_log(pMac, LOGE,
FL(" session %d is invalid or listen is disabled "),
p2pRemainonChn->sessionId);
goto error;
}
len = sizeof(tSirRemainOnChnReq) + pMac->p2pContext.probeRspIeLength;
if (len > 0xFFFF) {
/*In coming len for Msg is more then 16bit value */
sms_log(pMac, LOGE, FL(" Message length is very large, %d"),
len);
goto error;
}
pMsg = cdf_mem_malloc(len);
if (NULL == pMsg)
goto error;
else {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_INFO, "%s call",
__func__);
cdf_mem_set(pMsg, sizeof(tSirRemainOnChnReq), 0);
pMsg->messageType = eWNI_SME_REMAIN_ON_CHANNEL_REQ;
pMsg->length = (uint16_t) len;
cdf_mem_copy(pMsg->selfMacAddr, pSession->selfMacAddr.bytes,
sizeof(tSirMacAddr));
pMsg->chnNum = p2pRemainonChn->u.remainChlCmd.chn;
pMsg->phyMode = p2pRemainonChn->u.remainChlCmd.phyMode;
pMsg->duration = p2pRemainonChn->u.remainChlCmd.duration;
pMsg->sessionId = p2pRemainonChn->sessionId;
pMsg->isProbeRequestAllowed =
p2pRemainonChn->u.remainChlCmd.isP2PProbeReqAllowed;
pMsg->scan_id = p2pRemainonChn->u.remainChlCmd.scan_id;
if (pMac->p2pContext.probeRspIeLength)
cdf_mem_copy((void *)pMsg->probeRspIe,
(void *)pMac->p2pContext.probeRspIe,
pMac->p2pContext.probeRspIeLength);
status = cds_send_mb_message_to_mac(pMsg);
}
error:
if (CDF_STATUS_E_FAILURE == status)
csr_release_roc_req_cmd(pMac);
return status;
}
/*------------------------------------------------------------------
*
* handle LIM remain on channel rsp: Success/failure.
*
*------------------------------------------------------------------*/
CDF_STATUS sme_remain_on_chn_rsp(tpAniSirGlobal pMac, uint8_t *pMsg)
{
CDF_STATUS status = CDF_STATUS_SUCCESS;
tListElem *pEntry = NULL;
tSmeCmd *pCommand = NULL;
bool fFound;
remainOnChanCallback callback = NULL;
struct sir_roc_rsp *rsp = (struct sir_roc_rsp *)pMsg;
csr_get_active_scan_entry(pMac, rsp->scan_id, &pEntry);
if (!pEntry) {
sms_log(pMac, LOGE, FL("No cmd found in active list."));
return status;
}
pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link);
if (eSmeCommandRemainOnChannel != pCommand->command)
return status;
callback = pCommand->u.remainChlCmd.callback;
if (callback)
callback(pMac, pCommand->u.remainChlCmd.callbackCtx,
rsp->status, rsp->scan_id);
fFound = csr_ll_remove_entry(&pMac->sme.smeScanCmdActiveList, pEntry,
LL_ACCESS_LOCK);
if (fFound) {
/* Now put this command back on the avilable command list */
sme_release_command(pMac, pCommand);
}
sme_process_pending_queue(pMac);
return status;
}
/*------------------------------------------------------------------
*
* Handle the Mgmt frm ind from LIM and forward to HDD.
*
*------------------------------------------------------------------*/
CDF_STATUS sme_mgmt_frm_ind(tHalHandle hHal, tpSirSmeMgmtFrameInd pSmeMgmtFrm)
{
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
CDF_STATUS status = CDF_STATUS_SUCCESS;
tCsrRoamInfo pRoamInfo = { 0 };
uint8_t i = 0;
uint32_t SessionId = pSmeMgmtFrm->sessionId;
pRoamInfo.nFrameLength =
pSmeMgmtFrm->mesgLen - sizeof(tSirSmeMgmtFrameInd);
pRoamInfo.pbFrames = pSmeMgmtFrm->frameBuf;
pRoamInfo.frameType = pSmeMgmtFrm->frameType;
pRoamInfo.rxChan = pSmeMgmtFrm->rxChan;
pRoamInfo.rxRssi = pSmeMgmtFrm->rxRssi;
if (CSR_IS_SESSION_ANY(SessionId)) {
for (i = 0; i < CSR_ROAM_SESSION_MAX; i++) {
if (CSR_IS_SESSION_VALID(pMac, i)) {
SessionId = i;
break;
}
}
}
if (i == CSR_ROAM_SESSION_MAX) {
sms_log(pMac, LOGE, FL("No valid sessions found."));
return CDF_STATUS_E_FAILURE;
}
/* forward the mgmt frame to HDD */
csr_roam_call_callback(pMac, SessionId, &pRoamInfo, 0,
eCSR_ROAM_INDICATE_MGMT_FRAME, 0);
return status;
}
/*------------------------------------------------------------------
*
* Handle the remain on channel ready indication from PE
*
*------------------------------------------------------------------*/
CDF_STATUS sme_remain_on_chn_ready(tHalHandle hHal, uint8_t *pMsg)
{
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
CDF_STATUS status = CDF_STATUS_SUCCESS;
tListElem *pEntry = NULL;
tSmeCmd *pCommand = NULL;
tCsrRoamInfo RoamInfo;
struct sir_roc_rsp *rsp = (struct sir_roc_rsp *)pMsg;
csr_get_active_scan_entry(pMac, rsp->scan_id, &pEntry);
if (!pEntry) {
sms_log(pMac, LOGE, FL("No cmd found in active list."));
return status;
}
sms_log(pMac, LOG1,
FL("Ready Ind %d %d"), rsp->session_id, rsp->scan_id);
pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link);
if (eSmeCommandRemainOnChannel == pCommand->command) {
RoamInfo.roc_scan_id = rsp->scan_id;
/* forward the indication to HDD */
RoamInfo.pRemainCtx = pCommand->u.remainChlCmd.callbackCtx;
csr_roam_call_callback(pMac, rsp->session_id,
&RoamInfo, 0,
eCSR_ROAM_REMAIN_CHAN_READY, 0);
}
return status;
}
CDF_STATUS sme_send_action_cnf(tHalHandle hHal, uint8_t *pMsg)
{
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
CDF_STATUS status = CDF_STATUS_SUCCESS;
tCsrRoamInfo RoamInfo;
tSirSmeRsp *pSmeRsp = (tSirSmeRsp *) pMsg;
/* forward the indication to HDD */
/* RoamInfo can be passed as NULL....todo */
csr_roam_call_callback(pMac, pSmeRsp->sessionId, &RoamInfo, 0,
eCSR_ROAM_SEND_ACTION_CNF,
(pSmeRsp->statusCode == eSIR_SME_SUCCESS) ? 0 :
eCSR_ROAM_RESULT_SEND_ACTION_FAIL);
return status;
}
CDF_STATUS sme_p2p_open(tHalHandle hHal)
{
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
CDF_STATUS status = CDF_STATUS_SUCCESS;
/* If static structure is too big, Need to change this function to allocate memory dynamically */
cdf_mem_zero(&pMac->p2pContext, sizeof(tp2pContext));
if (!CDF_IS_STATUS_SUCCESS(status)) {
sme_p2p_close(hHal);
}
return status;
}
CDF_STATUS p2p_stop(tHalHandle hHal)
{
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
if (pMac->p2pContext.probeRspIe) {
cdf_mem_free(pMac->p2pContext.probeRspIe);
pMac->p2pContext.probeRspIe = NULL;
}
pMac->p2pContext.probeRspIeLength = 0;
return CDF_STATUS_SUCCESS;
}
CDF_STATUS sme_p2p_close(tHalHandle hHal)
{
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
if (pMac->p2pContext.probeRspIe) {
cdf_mem_free(pMac->p2pContext.probeRspIe);
pMac->p2pContext.probeRspIe = NULL;
}
pMac->p2pContext.probeRspIeLength = 0;
return CDF_STATUS_SUCCESS;
}
tSirRFBand get_rf_band(uint8_t channel)
{
if ((channel >= SIR_11A_CHANNEL_BEGIN) &&
(channel <= SIR_11A_CHANNEL_END))
return SIR_BAND_5_GHZ;
if ((channel >= SIR_11B_CHANNEL_BEGIN) &&
(channel <= SIR_11B_CHANNEL_END))
return SIR_BAND_2_4_GHZ;
return SIR_BAND_UNKNOWN;
}
/* ---------------------------------------------------------------------------
\fn p2p_remain_on_channel
\brief API to post the remain on channel command.
\param hHal - The handle returned by mac_open.
\param sessinId - HDD session ID.
\param channel - Channel to remain on channel.
\param duration - Duration for which we should remain on channel
\param callback - callback function.
\param pContext - argument to the callback function
\return CDF_STATUS
-------------------------------------------------------------------------------*/
CDF_STATUS p2p_remain_on_channel(tHalHandle hHal, uint8_t sessionId,
uint8_t channel, uint32_t duration,
remainOnChanCallback callback,
void *pContext, uint8_t isP2PProbeReqAllowed,
uint32_t scan_id)
{
CDF_STATUS status = CDF_STATUS_SUCCESS;
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
tSmeCmd *pRemainChlCmd = NULL;
uint32_t phyMode;
pRemainChlCmd = sme_get_command_buffer(pMac);
if (pRemainChlCmd == NULL)
return CDF_STATUS_E_FAILURE;
if (SIR_BAND_5_GHZ == get_rf_band(channel)) {
phyMode = WNI_CFG_PHY_MODE_11A;
} else {
phyMode = WNI_CFG_PHY_MODE_11G;
}
cfg_set_int(pMac, WNI_CFG_PHY_MODE, phyMode);
do {
/* call set in context */
pRemainChlCmd->command = eSmeCommandRemainOnChannel;
pRemainChlCmd->sessionId = sessionId;
pRemainChlCmd->u.remainChlCmd.chn = channel;
pRemainChlCmd->u.remainChlCmd.duration = duration;
pRemainChlCmd->u.remainChlCmd.isP2PProbeReqAllowed =
isP2PProbeReqAllowed;
pRemainChlCmd->u.remainChlCmd.callback = callback;
pRemainChlCmd->u.remainChlCmd.callbackCtx = pContext;
pRemainChlCmd->u.remainChlCmd.scan_id = scan_id;
/* Put it at the head of the Q if we just finish finding the peer and ready to send a frame */
status = csr_queue_sme_command(pMac, pRemainChlCmd, false);
} while (0);
sms_log(pMac, LOGW, "exiting function %s", __func__);
return (status);
}
CDF_STATUS p2p_send_action(tHalHandle hHal, uint8_t sessionId,
const uint8_t *pBuf, uint32_t len, uint16_t wait,
bool noack, uint16_t channel_freq)
{
CDF_STATUS status = CDF_STATUS_SUCCESS;
tSirMbMsgP2p *pMsg;
uint16_t msgLen;
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_INFO_MED,
" %s sends action frame", __func__);
msgLen = (uint16_t) ((sizeof(tSirMbMsgP2p)) + len);
pMsg = cdf_mem_malloc(msgLen);
if (NULL == pMsg)
status = CDF_STATUS_E_NOMEM;
else {
cdf_mem_set((void *)pMsg, msgLen, 0);
pMsg->type = eWNI_SME_SEND_ACTION_FRAME_IND;
pMsg->msgLen = msgLen;
pMsg->sessionId = sessionId;
pMsg->noack = noack;
pMsg->channel_freq = channel_freq;
pMsg->wait = (uint16_t) wait;
cdf_mem_copy(pMsg->data, pBuf, len);
status = cds_send_mb_message_to_mac(pMsg);
}
return (status);
}
CDF_STATUS p2p_cancel_remain_on_channel(tHalHandle hHal,
uint8_t sessionId, uint32_t scan_id)
{
CDF_STATUS status = CDF_STATUS_SUCCESS;
tSirMbMsgP2p *pMsg;
uint16_t msgLen;
/* Need to check session ID to support concurrency */
msgLen = (uint16_t) (sizeof(tSirMbMsgP2p));
pMsg = cdf_mem_malloc(msgLen);
if (NULL == pMsg)
status = CDF_STATUS_E_NOMEM;
else {
cdf_mem_set((void *)pMsg, msgLen, 0);
pMsg->type = eWNI_SME_ABORT_REMAIN_ON_CHAN_IND;
pMsg->msgLen = msgLen;
pMsg->sessionId = sessionId;
pMsg->scan_id = scan_id;
status = cds_send_mb_message_to_mac(pMsg);
}
return (status);
}
CDF_STATUS p2p_set_ps(tHalHandle hHal, tP2pPsConfig *pNoA)
{
tpP2pPsConfig pNoAParam;
tSirMsgQ msg;
CDF_STATUS status = CDF_STATUS_SUCCESS;
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
pNoAParam = cdf_mem_malloc(sizeof(tP2pPsConfig));
if (NULL == pNoAParam)
status = CDF_STATUS_E_NOMEM;
else {
cdf_mem_set(pNoAParam, sizeof(tP2pPsConfig), 0);
cdf_mem_copy(pNoAParam, pNoA, sizeof(tP2pPsConfig));
msg.type = eWNI_SME_UPDATE_NOA;
msg.bodyval = 0;
msg.bodyptr = pNoAParam;
lim_post_msg_api(pMac, &msg);
}
return status;
}