/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 *  * Neither the name of The Linux Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "aniGlobal.h"
#include "smeInside.h"
#include "csrInsideApi.h"
#include "smsDebug.h"
#include "macTrace.h"
#include "csrNeighborRoam.h"
#include "csr_roam_mbb.h"
#include "csrInternal.h"
#include "wlan_qct_wda.h"

eHalStatus csr_register_roaming_mbb_callback(tpAniSirGlobal mac);

#define PREAUTH_REASSOC_MBB_TIMER_VALUE    60

#define CSR_NEIGHBOR_ROAM_STATE_TRANSITION(newState)\
{\
    mac->roam.neighborRoamInfo.prevNeighborRoamState = mac->roam.neighborRoamInfo.neighborRoamState;\
    mac->roam.neighborRoamInfo.neighborRoamState = newState;\
    VOS_TRACE (VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_DEBUG, \
               FL("Neighbor Roam Transition from state %s ==> %s"), \
               csrNeighborRoamStateToString (mac->roam.neighborRoamInfo.prevNeighborRoamState), \
               csrNeighborRoamStateToString (newState));\
}

/**
 * csr_roam_issue_preauth_reassoc_req() -Prepares preauth request
 * @hal: HAL context
 * @session_id: session id
 * @bss_description: BSS description
 *
 * This function prepares preauth request and sends request to PE
 *
 * Return: eHAL_STATUS_SUCCESS on success,
 *           : eHAL_STATUS_RESOURCES when resource allocation is failure
 *           : eHAL_STATUS_FAILURE otherwise
 */
eHalStatus csr_roam_issue_preauth_reassoc_req(tHalHandle hal,
                     tANI_U32 session_id, tpSirBssDescription bss_description)
{
    tpAniSirGlobal mac = PMAC_STRUCT(hal);
    tpSirFTPreAuthReq pre_auth_req;
    tANI_U16 auth_req_len = 0;
    tCsrRoamSession *session = CSR_GET_SESSION(mac, session_id);
    tpCsrNeighborRoamControlInfo neighbor_roam_info =
                                          &mac->roam.neighborRoamInfo;
    eHalStatus status;

    if (session == NULL) {
        smsLog(mac, LOGE, FL("Invalid Session ID %d"), session_id);
        return eHAL_STATUS_FAILURE;
    }

    auth_req_len = sizeof(tSirFTPreAuthReq);
    pre_auth_req = (tpSirFTPreAuthReq)vos_mem_malloc(auth_req_len);
    if (NULL == pre_auth_req) {
        smsLog(mac, LOGE,
               FL("Memory allocation for Preauth request failed"));
        return eHAL_STATUS_RESOURCES;
    }

    /*
     * If neighborRoamState is eCSR_NEIGHBOR_ROAM_STATE_INIT
     * by the time this API is invoked, disconnect would have happened.
     * So, need to proceed further.
     */
    if (mac->roam.neighborRoamInfo.neighborRoamState ==
                                          eCSR_NEIGHBOR_ROAM_STATE_INIT) {
        smsLog(mac, LOGE, FL("neighborRoamState %d"),
                          mac->roam.neighborRoamInfo.neighborRoamState);
        return eHAL_STATUS_FAILURE;
    }

    /*
     * Save the SME Session ID here. We need it while processing
     * the preauth response.
     */
    mac->ft.ftSmeContext.smeSessionId = session_id;
    vos_mem_zero(pre_auth_req, auth_req_len);

    pre_auth_req->pbssDescription = (tpSirBssDescription)vos_mem_malloc(
            sizeof(bss_description->length) + bss_description->length);
    if (NULL == pre_auth_req->pbssDescription) {
        smsLog(mac, LOGE,
               FL("Unable to allocate memory for preauth bss description"));
        return eHAL_STATUS_RESOURCES;
    }

    pre_auth_req->messageType =
                     pal_cpu_to_be16(eWNI_SME_MBB_PRE_AUTH_REASSOC_REQ);

    pre_auth_req->preAuthchannelNum = bss_description->channelId;

    vos_mem_copy((void *)&pre_auth_req->currbssId,
                 (void *)session->connectedProfile.bssid, sizeof(tSirMacAddr));
    vos_mem_copy((void *)&pre_auth_req->preAuthbssId,
                 (void *)bss_description->bssId, sizeof(tSirMacAddr));

    vos_mem_copy(pre_auth_req->pbssDescription, bss_description,
                 sizeof(bss_description->length) + bss_description->length);
    pre_auth_req->length = pal_cpu_to_be16(auth_req_len);

    /* Register mbb callback */
    smsLog(mac, LOG1, FL("Registering mbb callback"));
    csr_register_roaming_mbb_callback(mac);

    csrReleaseProfile(mac, &neighbor_roam_info->csrNeighborRoamProfile);

    /* Copy current profile to be used in csr_update_roamed_info_mbb */
    status = csrRoamCopyProfile(mac,
                                &neighbor_roam_info->csrNeighborRoamProfile,
                                session->pCurRoamProfile);
    if(!HAL_STATUS_SUCCESS(status)) {
       smsLog(mac, LOGE, FL("Profile copy failed"));
       return status;
    }

    return palSendMBMessage(mac->hHdd, pre_auth_req);
}

/**
 * csr_neighbor_roam_issue_preauth_reassoc() -issues  preauth_reassoc request
 * @mac: MAC context
 *
 * This function issues preauth_reassoc request to PE with the 1st AP
 * entry in the roamable AP list
 *
 * Return: eHAL_STATUS_SUCCESS on success, eHAL_STATUS_FAILURE otherwise
 */
eHalStatus csr_neighbor_roam_issue_preauth_reassoc(tpAniSirGlobal mac)
{
    tpCsrNeighborRoamControlInfo neighbor_roam_info =
                                           &mac->roam.neighborRoamInfo;
    eHalStatus status = eHAL_STATUS_SUCCESS;
    tpCsrNeighborRoamBSSInfo neighbor_bss_node;
    tCsrRoamInfo roam_info;

    VOS_ASSERT(neighbor_roam_info->FTRoamInfo.preauthRspPending ==
                                                         eANI_BOOLEAN_FALSE);

    neighbor_bss_node = csrNeighborRoamGetRoamableAPListNextEntry(mac,
                               &neighbor_roam_info->roamableAPList, NULL);

    if (neighbor_bss_node == NULL)
    {
        smsLog(mac, LOGE, FL("Roamable AP list is empty"));
        csrRoamOffloadScan(mac, ROAM_SCAN_OFFLOAD_RESTART,
                            REASON_NO_CAND_FOUND_OR_NOT_ROAMING_NOW);
        return eHAL_STATUS_FAILURE;
    }
    else
    {
        /*
         * Set is_preauth_lfr_mbb which will be checked in
         * different API's.
         */
        mac->ft.ftSmeContext.is_preauth_lfr_mbb = true;
        smsLog(mac, LOG1, FL("is_preauth_lfr_mbb %d"),
                mac->ft.ftSmeContext.is_preauth_lfr_mbb);

        status = csrRoamEnqueuePreauth(mac,
                 neighbor_roam_info->csrSessionId,
                 neighbor_bss_node->pBssDescription,
                 ecsr_mbb_perform_preauth_reassoc,
                 eANI_BOOLEAN_TRUE);

        smsLog(mac, LOG1, FL("Before Pre-Auth: BSSID "MAC_ADDRESS_STR", Ch:%d"),
               MAC_ADDR_ARRAY(neighbor_bss_node->pBssDescription->bssId),
               neighbor_bss_node->pBssDescription->channelId);

        if (eHAL_STATUS_SUCCESS != status)
        {
            smsLog(mac, LOGE,
                   FL("Send Preauth request to PE failed with status %d"),
                   status);
            return status;
        }
    }

    neighbor_roam_info->FTRoamInfo.preauthRspPending = eANI_BOOLEAN_TRUE;

    CSR_NEIGHBOR_ROAM_STATE_TRANSITION(eCSR_NEIGHBOR_ROAM_STATE_MBB_PREAUTH_REASSOC)

    if (csrRoamIsFastRoamEnabled(mac, CSR_SESSION_ID_INVALID))
    {
        smsLog(mac, LOG1, FL("Invoking eCSR_ROAM_PMK_NOTIFY"));
        vos_mem_copy((void *)&roam_info.bssid,
                     (void *)neighbor_bss_node->pBssDescription->bssId,
                     sizeof(tCsrBssid));
        csrRoamCallCallback(mac, neighbor_roam_info->csrSessionId, &roam_info,
                            0, eCSR_ROAM_PMK_NOTIFY, 0);
    }
    return status;
}

/**
 * csr_stop_preauth_reassoc_mbb_timer() -stops preauth_reassoc timer
 * @mac: MAC context
 *
 * This function stops preauth_reassoc timer
 *
 */
void csr_stop_preauth_reassoc_mbb_timer(tpAniSirGlobal mac)
{
    VOS_STATUS vos_status = VOS_STATUS_SUCCESS;

    if (mac->roam.neighborRoamInfo.is_pre_auth_reassoc_mbb_timer_started) {
        vos_status =
            vos_timer_stop(&mac->ft.ftSmeContext.pre_auth_reassoc_mbb_timer);
        mac->roam.neighborRoamInfo.is_pre_auth_reassoc_mbb_timer_started =
                                                                       false;
    }
}


/**
 * csr_preauth_reassoc_mbb_timer_callback() -preauth_reassoc timer callback
 * @mac: MAC context
 *
 * This function issues preauth_reassoc with another roamable entry
 *
 */
void csr_preauth_reassoc_mbb_timer_callback(void *context)
{
    tpAniSirGlobal mac = (tpAniSirGlobal)context;

    mac->roam.neighborRoamInfo.is_pre_auth_reassoc_mbb_timer_started = 0;

    smsLog(mac, LOG1, FL("is_pre_auth_reassoc_mbb_timer_started %d"),
           mac->roam.neighborRoamInfo.is_pre_auth_reassoc_mbb_timer_started);

    csr_neighbor_roam_issue_preauth_reassoc(mac);
}


/**
 * csr_roam_dequeue_preauth_reassoc() -Dequeues
 * ecsr_mbb_perform_preauth_reassoc
 * @mac: MAC context
 *
 * This function dequeues ecsr_mbb_perform_preauth_reassoc
 *
 */
eHalStatus csr_roam_dequeue_preauth_reassoc(tpAniSirGlobal mac)
{
    tListElem *entry;
    tSmeCmd *command;
    entry = csrLLPeekHead(&mac->sme.smeCmdActiveList, LL_ACCESS_LOCK);
    if (entry) {
        command = GET_BASE_ADDR(entry, tSmeCmd, Link);
        if ((eSmeCommandRoam == command->command) &&
            (ecsr_mbb_perform_preauth_reassoc ==
                                    command->u.roamCmd.roamReason)) {
            smsLog(mac, LOG1, FL("DQ-Command = %d, Reason = %d"),
                    command->command, command->u.roamCmd.roamReason);
            if (csrLLRemoveEntry( &mac->sme.smeCmdActiveList,
                                       entry, LL_ACCESS_LOCK)) {
                csrReleaseCommandPreauth( mac, command );
            }
        } else {
            smsLog(mac, LOGE, FL("Command = %d, Reason = %d "),
                    command->command, command->u.roamCmd.roamReason);
        }
    }
    else {
        smsLog(mac, LOGE,
               FL("pEntry NULL for eWNI_SME_MBB_PRE_AUTH_REASSOC_RSP"));
    }
    smeProcessPendingQueue( mac );
    return eHAL_STATUS_SUCCESS;
}

/**
 * csr_neighbor_roam_preauth_reassoc_rsp_handler() -handles preauth
 * reassoc response
 * @mac: MAC context
 * @lim_status: status of preauth reassoc response from lim
 * @bss_description: bss description pointer
 *
 * This function handles preauth_reassoc response from PE. When
 * preauth_reassoc response failure is received, preauth reassoc
 * with new candidate will be attempted. In success case, candidate will be
 * removed from roamable entry.
 *
 */
eHalStatus
csr_neighbor_roam_preauth_reassoc_rsp_handler(tpAniSirGlobal mac,
          tSirRetStatus lim_status, tSirBssDescription **bss_description)
{
    tpCsrNeighborRoamControlInfo neighbor_roam_info =
                                      &mac->roam.neighborRoamInfo;
    eHalStatus status = eHAL_STATUS_SUCCESS;
    eHalStatus preauth_processed = eHAL_STATUS_SUCCESS;
    tpCsrNeighborRoamBSSInfo preauth_rsp_node = NULL;

    if (eANI_BOOLEAN_FALSE ==
                neighbor_roam_info->FTRoamInfo.preauthRspPending) {
       /*
        * This can happen when we disconnect immediately after sending
        * a pre-auth request. During processing of the disconnect command,
        * we would have reset preauthRspPending and transitioned to INIT state.
        */
       smsLog(mac, LOGE,
              FL("Unexpected pre-auth response in state %d"),
              neighbor_roam_info->neighborRoamState);
       preauth_processed = eHAL_STATUS_FAILURE;
       goto DEQ_PREAUTH;
    }

    if ((neighbor_roam_info->neighborRoamState !=
                            eCSR_NEIGHBOR_ROAM_STATE_MBB_PREAUTH_REASSOC)) {
        smsLog(mac, LOGE,
               FL("Preauth response received in state %s"),
               macTraceGetNeighbourRoamState(
                      neighbor_roam_info->neighborRoamState));
        preauth_processed = eHAL_STATUS_FAILURE;
        goto DEQ_PREAUTH;
    }

    neighbor_roam_info->FTRoamInfo.preauthRspPending = eANI_BOOLEAN_FALSE;

    if (eSIR_SUCCESS == lim_status)
        preauth_rsp_node = csrNeighborRoamGetRoamableAPListNextEntry(mac,
                                  &neighbor_roam_info->roamableAPList, NULL);

    if ((eSIR_SUCCESS == lim_status) && (NULL != preauth_rsp_node)) {
        smsLog(mac, LOG1, FL("MBB Reassoc completed successfully"));

        smsLog(mac, LOG1, FL("After MBB reassoc BSSID "MAC_ADDRESS_STR" Ch %d"),
               MAC_ADDR_ARRAY(preauth_rsp_node->pBssDescription->bssId),
               preauth_rsp_node->pBssDescription->channelId);

        /* Memory will be freed in caller of this */
        *bss_description = (tpSirBssDescription)vos_mem_malloc(
                           sizeof(preauth_rsp_node->pBssDescription->length) +
                           preauth_rsp_node->pBssDescription->length);
        if (NULL == *bss_description) {
            smsLog(mac, LOGE,
                   FL("Unable to allocate memory for preauth bss description"));
            preauth_processed = eHAL_STATUS_RESOURCES;
            goto DEQ_PREAUTH;
        }

        vos_mem_copy(*bss_description, preauth_rsp_node->pBssDescription,
                     sizeof(preauth_rsp_node->pBssDescription->length) +
                     preauth_rsp_node->pBssDescription->length);

        /*
        * MBB Reassoc competer successfully. Insert the preauthenticated
        * node to tail of preAuthDoneList
        */
        csrNeighborRoamRemoveRoamableAPListEntry(mac,
                         &neighbor_roam_info->roamableAPList, preauth_rsp_node);
        csrLLInsertTail(&neighbor_roam_info->FTRoamInfo.preAuthDoneList,
                                  &preauth_rsp_node->List, LL_ACCESS_LOCK);
        return eHAL_STATUS_SUCCESS;
    } else {
        tpCsrNeighborRoamBSSInfo    neighbor_bss_node = NULL;
        tListElem                   *entry;

        /*
        * Pre-auth failed. Add the bssId to the preAuth failed list MAC Address.
        * Also remove the AP from roamable AP list. The one in the head of the
        * list should be one with which we issued pre-auth and failed.
        */
        entry = csrLLRemoveHead(&neighbor_roam_info->roamableAPList,
                                  LL_ACCESS_LOCK);
        if(entry) {
           neighbor_bss_node = GET_BASE_ADDR(entry,
                                            tCsrNeighborRoamBSSInfo, List);
           /*
            * Add the BSSID to pre-auth fail list if it is
            * not requested by HDD
            */
           status = csrNeighborRoamAddBssIdToPreauthFailList(mac,
                                 neighbor_bss_node->pBssDescription->bssId);

           smsLog(mac, LOG1,
               FL("MBB reassoc failed BSSID "MAC_ADDRESS_STR" Ch:%d status %d"),
               MAC_ADDR_ARRAY(neighbor_bss_node->pBssDescription->bssId),
               neighbor_bss_node->pBssDescription->channelId, lim_status);

           /* Now we can free this node */
           csrNeighborRoamFreeNeighborRoamBSSNode(mac, neighbor_bss_node);
        }

        /*
        * Move state to Connected. Connected state here signifies connection
        * with current AP as preauth failed with roamable AP. Still driver has
        * connection with current AP.
        */
        CSR_NEIGHBOR_ROAM_STATE_TRANSITION(eCSR_NEIGHBOR_ROAM_STATE_CONNECTED)

        /* Start a timer to issue preauth_reassoc request for the next entry*/
        status = vos_timer_start(&mac->ft.ftSmeContext.
                   pre_auth_reassoc_mbb_timer, PREAUTH_REASSOC_MBB_TIMER_VALUE);
        if (eHAL_STATUS_SUCCESS != status) {
            smsLog(mac, LOGE,
                   FL("pre_auth_reassoc_mbb_timer start failed status %d"),
                   status);
            preauth_processed = eHAL_STATUS_FAILURE;
            goto DEQ_PREAUTH;
        }
        mac->roam.neighborRoamInfo.is_pre_auth_reassoc_mbb_timer_started = true;
        smsLog(mac, LOG1, FL("is_pre_auth_reassoc_mbb_timer_started %d"),
           mac->roam.neighborRoamInfo.is_pre_auth_reassoc_mbb_timer_started);
        preauth_processed = eHAL_STATUS_SUCCESS;
    }

DEQ_PREAUTH:
    csr_roam_dequeue_preauth_reassoc(mac);
    return preauth_processed;
}

eHalStatus csr_update_roamed_info_mbb(tHalHandle hal,
     tpSirBssDescription bss_description, tpSirFTPreAuthRsp pre_auth_rsp)
{
    eHalStatus status;
    tpAniSirGlobal mac = PMAC_STRUCT(hal);
    tDot11fBeaconIEs *ies_local = NULL;
    tCsrRoamSession *session = NULL;
    tCsrRoamProfile *profile;
    tSirMacAddr broadcast_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
    tANI_U32 key_timeout_interval;
    tCsrRoamInfo roam_info;
    sme_QosAssocInfo assoc_info;
    tANI_U32 len;
    tANI_U8 sme_session_id = pre_auth_rsp->smeSessionId;
    tANI_U8 acm_mask = 0;

    /* Get profile */
    session = CSR_GET_SESSION(mac, sme_session_id);
    if (session == NULL) {
        smsLog(mac, LOGE,
               FL("Invalid Session ID %d"), sme_session_id);
        return eHAL_STATUS_FAILURE;
    }
    profile = vos_mem_malloc(sizeof(*profile));
    if (NULL == profile) {
        smsLog(mac, LOGE, FL("Memory allocation failure for profile"));
        return eHAL_STATUS_FAILURE;
    }

    /*
     * session->pCurRoamProfile is copied into csrNeighborRoamProfile
     * in csr_roam_issue_preauth_reassoc_req as there is a chance of
     * session->pCurRoamProfile getting freed when disconnect is issued
     * from upper layer.
     */
    status = csrRoamCopyProfile(mac, profile,
                  &mac->roam.neighborRoamInfo.csrNeighborRoamProfile);
    if(!HAL_STATUS_SUCCESS(status)) {
       smsLog(mac, LOGE, FL("Profile copy failed"));
       vos_mem_free(profile);
       return status;
    }

    profile->negotiatedAuthType =
       mac->roam.roamSession[sme_session_id].connectedProfile.AuthType;
    profile->negotiatedUCEncryptionType =
       mac->roam.roamSession[sme_session_id].connectedProfile.EncryptionType;
    profile->mcEncryptionType =
       mac->roam.roamSession[sme_session_id].connectedProfile.mcEncryptionInfo;
    profile->negotiatedMCEncryptionType =
       mac->roam.roamSession[sme_session_id].connectedProfile.mcEncryptionType;

    smsLog(mac, LOG1,
           FL("AuthType %d UCEType %d Entries %d encryptionType %d MCEType %d"),
           profile->negotiatedAuthType,
           profile->negotiatedUCEncryptionType,
           profile->mcEncryptionType.numEntries,
           profile->mcEncryptionType.encryptionType[0],
           profile->negotiatedMCEncryptionType);

    /* Get IE's */
    status = csrGetParsedBssDescriptionIEs(mac, bss_description, &ies_local);
    if (!HAL_STATUS_SUCCESS(status)) {
        smsLog(mac, LOGE,
               FL("csrGetParsedBssDescriptionIEs failed"));
        vos_mem_free(profile);
        return status;
    }
    if(ies_local == NULL) {
       smsLog(mac, LOGE,
              FL("ies_local is NULL "));
       vos_mem_free(profile);
       return eHAL_STATUS_FAILURE;
    }

    vos_mem_set(&roam_info, sizeof(roam_info), 0);

    csrRoamStopNetwork(mac, sme_session_id, profile,
                                          bss_description, ies_local);

    session->connectState = eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED;

    csrRoamSaveConnectedInfomation(mac, sme_session_id, profile,
                                                  bss_description, ies_local);
    csrRoamSaveSecurityRspIE(mac, sme_session_id,
                             profile->negotiatedAuthType, bss_description,
                             ies_local);

    csrRoamStateChange(mac, eCSR_ROAMING_STATE_JOINED, sme_session_id);

    /*
     * If abortConnection is set, it implies that disconnect in progress
     * from upper layer. So there is no point in proceeding further.
     * By this point, pConnectBssDesc is updated in
     * csrRoamSaveConnectedInfomation. This pConnectBssDesc is used
     * during processing of disconnect in CSR and this has new BSSID info.
     * By this time, original AP related info was cleaned up in LIM. So,
     * disconnect queued while roaming in progress will take care of
     * cleaning up roamed AP related info in LIM.
     */
    if (session->abortConnection) {
        smsLog(mac, LOGE, FL("Disconnect in progress"));

        smsLog(mac, LOGE, FL("MBB reassoc BSSID "MAC_ADDRESS_STR" Ch %d"),
               MAC_ADDR_ARRAY(bss_description->bssId),
               bss_description->channelId);

        vos_mem_free(profile);
        return eHAL_STATUS_FAILURE;
    }

    if(CSR_IS_ENC_TYPE_STATIC(profile->negotiatedUCEncryptionType) &&
                                        !profile->bWPSAssociation) {
       /*
        * Issue the set Context request to LIM to establish the
        * Unicast STA context
        */
       if(!HAL_STATUS_SUCCESS(csrRoamIssueSetContextReq(mac,
                 sme_session_id,
                 profile->negotiatedUCEncryptionType,
                 bss_description, &(bss_description->bssId), FALSE, TRUE,
                 eSIR_TX_RX, 0, 0, NULL, 0))) {
          smsLog(mac, LOGE, FL("Set context for unicast fail"));
          csrRoamSubstateChange(mac, eCSR_ROAM_SUBSTATE_NONE,
                                        sme_session_id);
       }
       /*
        * Issue the set Context request to LIM to establish the
        * Broadcast STA context
        */
       csrRoamIssueSetContextReq(mac, sme_session_id,
                                 profile->negotiatedMCEncryptionType,
                                 bss_description, &broadcast_mac,
                                 FALSE, FALSE, eSIR_TX_RX, 0, 0, NULL, 0);
    } else if (!session->abortConnection) {
       /* Need to wait for supplicant authtication */
       roam_info.fAuthRequired = eANI_BOOLEAN_TRUE;
       /* Set the subestate to WaitForKey in case authentiation is needed */
       csrRoamSubstateChange(mac, eCSR_ROAM_SUBSTATE_WAIT_FOR_KEY,
                                            sme_session_id);

       if(profile->bWPSAssociation)
          key_timeout_interval = CSR_WAIT_FOR_WPS_KEY_TIMEOUT_PERIOD;
       else
          key_timeout_interval = CSR_WAIT_FOR_KEY_TIMEOUT_PERIOD;

       /* Save sessionId in case of timeout */
       mac->roam.WaitForKeyTimerInfo.sessionId = sme_session_id;
       /*
        * This time should be long enough for the rest of the
        * process plus setting key
        */
       if(!HAL_STATUS_SUCCESS(csrRoamStartWaitForKeyTimer(mac,
                                             key_timeout_interval))) {
          /* Reset our state so nothting is blocked */
          smsLog(mac, LOGE, FL("Failed to start pre-auth timer"));
          csrRoamSubstateChange(mac, eCSR_ROAM_SUBSTATE_NONE,
                                           sme_session_id);
       }
    }

    csrRoamFreeConnectedInfo(mac, &session->connectedInfo);

    assoc_info.pBssDesc = bss_description;
    assoc_info.pProfile = profile;

    len = pre_auth_rsp->roam_info->nBeaconLength +
          pre_auth_rsp->roam_info->nAssocReqLength +
          pre_auth_rsp->roam_info->nAssocRspLength;

    if(len) {
       session->connectedInfo.pbFrames = vos_mem_malloc(len);
       if (session->connectedInfo.pbFrames != NULL ) {
           vos_mem_copy(session->connectedInfo.pbFrames,
                        pre_auth_rsp->roam_info->pbFrames, len);
           session->connectedInfo.nAssocReqLength =
                           pre_auth_rsp->roam_info->nAssocReqLength;
           session->connectedInfo.nAssocRspLength =
                           pre_auth_rsp->roam_info->nAssocRspLength;
           session->connectedInfo.nBeaconLength =
                           pre_auth_rsp->roam_info->nBeaconLength;

           roam_info.nAssocReqLength = pre_auth_rsp->roam_info->nAssocReqLength;
           roam_info.nAssocRspLength = pre_auth_rsp->roam_info->nAssocRspLength;
           roam_info.nBeaconLength = pre_auth_rsp->roam_info->nBeaconLength;
           roam_info.pbFrames = session->connectedInfo.pbFrames;
       }
       session->connectedInfo.staId = pre_auth_rsp->roam_info->staId;
       roam_info.staId = pre_auth_rsp->roam_info->staId;
       roam_info.ucastSig = pre_auth_rsp->roam_info->ucastSig;
       roam_info.bcastSig = pre_auth_rsp->roam_info->bcastSig;
       roam_info.maxRateFlags = pre_auth_rsp->roam_info->maxRateFlags;
    }

    roam_info.statusCode = eSIR_SME_SUCCESS;
    roam_info.reasonCode = eSIR_SME_SUCCESS;
    roam_info.pBssDesc = bss_description;

    vos_mem_copy(&roam_info.bssid, &bss_description->bssId,
                                     sizeof(tCsrBssid));

    mac->roam.roamSession[sme_session_id].connectState =
                                     eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED;

    sme_QosCsrEventInd(mac, sme_session_id,
                            SME_QOS_CSR_HANDOFF_ASSOC_REQ, NULL);
    sme_QosCsrEventInd(mac, sme_session_id, SME_QOS_CSR_REASSOC_REQ, NULL);
    sme_QosCsrEventInd(mac, sme_session_id, SME_QOS_CSR_HANDOFF_COMPLETE, NULL);

    mac->roam.roamSession[sme_session_id].connectState =
                                    eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED;
    sme_QosCsrEventInd(mac, sme_session_id,
                              SME_QOS_CSR_REASSOC_COMPLETE, &assoc_info);


    acm_mask = sme_QosGetACMMask(mac, bss_description, NULL);

    session->connectedProfile.acm_mask = acm_mask;
    if(session->connectedProfile.modifyProfileFields.uapsd_mask) {
       smsLog(mac, LOGE, "uapsd_mask (0x%X) set, request UAPSD now",
              session->connectedProfile.modifyProfileFields.uapsd_mask);
              pmcStartUapsd(mac, NULL, NULL);
    }
    session->connectedProfile.dot11Mode = session->bssParams.uCfgDot11Mode;
    roam_info.u.pConnectedProfile = &session->connectedProfile;

    if(!IS_FEATURE_SUPPORTED_BY_FW(SLM_SESSIONIZATION) &&
                      (csrIsConcurrentSessionRunning(mac)))
       mac->roam.configParam.doBMPSWorkaround = 1;

    csr_roam_dequeue_preauth_reassoc(mac);

    csrRoamCallCallback(mac, sme_session_id, &roam_info, 0,
             eCSR_ROAM_ASSOCIATION_COMPLETION, eCSR_ROAM_RESULT_ASSOCIATED);

    csrResetPMKIDCandidateList(mac, sme_session_id);
#ifdef FEATURE_WLAN_WAPI
    csrResetBKIDCandidateList(mac, sme_session_id);
#endif

    if(!CSR_IS_WAIT_FOR_KEY(mac, sme_session_id)) {
        smsLog(mac, LOG1, "NO CSR_IS_WAIT_FOR_KEY -> csr_roam_link_up");
        csrRoamLinkUp(mac, session->connectedProfile.bssid);
    }

    if (pmcShouldBmpsTimerRun(mac)) {
        if(eANI_BOOLEAN_TRUE == roam_info.fAuthRequired) {
           mac->pmc.full_power_till_set_key = true;
           smsLog(mac, LOG1,
                  FL("full_power_till_set_key is made true"));
        }

        if (pmcStartTrafficTimer(mac, BMPS_TRAFFIC_TIMER_ALLOW_SECURITY_DHCP)
                    != eHAL_STATUS_SUCCESS)
            smsLog(mac, LOGE, FL("Cannot start BMPS Retry timer"));

        smsLog(mac, LOG1, FL("BMPS Retry Timer already running or started"));
    }

    vos_mem_free(pre_auth_rsp->roam_info->pbFrames);
    vos_mem_free(pre_auth_rsp->roam_info);
    vos_mem_free(profile);
    vos_mem_free(ies_local);

    return eHAL_STATUS_SUCCESS;
}


/**
 * csr_roam_preauth_rsp_mbb_processor() -handles
 * eWNI_SME_MBB_PRE_AUTH_REASSOC_RSP
 * @hal: HAL context
 *
 * This function invokes preauth reassoc response handler and
 * updates CSR with new connection information.
 *
 */
void csr_roam_preauth_rsp_mbb_processor(tHalHandle hal,
     tpSirFTPreAuthRsp pre_auth_rsp)
{
    tpAniSirGlobal mac = PMAC_STRUCT(hal);
    eHalStatus  status;
    tCsrRoamInfo roam_info;
    tpSirBssDescription  bss_description = NULL;
    tCsrRoamSession *session = NULL;

    mac->ft.ftSmeContext.is_preauth_lfr_mbb = false;
    smsLog(mac, LOG1, FL("is_preauth_lfr_mbb %d"),
                         mac->ft.ftSmeContext.is_preauth_lfr_mbb);

    /*
    * When reason is SIR_MBB_DISCONNECTED, cleanup CSR info
    * of connected AP.
    */
    if (pre_auth_rsp->reason == SIR_MBB_DISCONNECTED) {
        /* Dequeue ecsr_perform_preauth_reassoc */
        csr_roam_dequeue_preauth_reassoc(mac);

        vos_mem_zero(&roam_info, sizeof(tCsrRoamInfo));
        csrRoamCallCallback(mac, pre_auth_rsp->smeSessionId, &roam_info, 0,
                               eCSR_ROAM_FT_REASSOC_FAILED, eSIR_SME_SUCCESS);

        csrRoamComplete(mac, eCsrJoinFailure, NULL);
    }

    status = csr_neighbor_roam_preauth_reassoc_rsp_handler(mac,
                                       pre_auth_rsp->status, &bss_description);
    if (status != eHAL_STATUS_SUCCESS) {
        smsLog(mac, LOGE,FL("Preauth was not processed: %d SessionID: %d"),
                            status, pre_auth_rsp->smeSessionId);
        /*
         * Preauth_reassoc is dequeued already in
         * csr_neighbor_roam_preauth_reassoc_rsp_handler for error cases.
         */
        return;
    }

    session = CSR_GET_SESSION(mac, pre_auth_rsp->smeSessionId);
    if (session == NULL) {
        smsLog(mac, LOGE,
               FL("Invalid Session ID %d"), pre_auth_rsp->smeSessionId);
        return;
    }
    if (session->abortConnection) {
        smsLog(mac, LOGE,
               FL("Disconnect in progress, stop preauth/reassoc timer"));
        vos_timer_stop(&mac->ft.ftSmeContext.pre_auth_reassoc_mbb_timer);
    }

    /*
     * The below function calls/timers should be invoked only
     * if the pre-auth is successful. Also, Preauth_reassoc is dequeued
     * already in csr_neighbor_roam_preauth_reassoc_rsp_handler.
     */
    if (VOS_STATUS_SUCCESS != (VOS_STATUS)pre_auth_rsp->status)
        return;

    mac->ft.ftSmeContext.FTState = eFT_AUTH_COMPLETE;

    /* Save the received response */
    vos_mem_copy((void *)&mac->ft.ftSmeContext.preAuthbssId,
                 (void *)pre_auth_rsp->preAuthbssId, sizeof(tCsrBssid));


    /*
     * bss_description is updated in
     * csr_neighbor_roam_preauth_reassoc_rsp_handler
     */
    if (NULL == bss_description) {
        smsLog(mac, LOGE,
               FL("bss description is NULL"));
        goto DEQ_PREAUTH;
    }

    /* Update CSR for new connection */
    status = csr_update_roamed_info_mbb(hal, bss_description, pre_auth_rsp);
    /* In success case preauth reassoc is dequeued in
     * csr_update_roamed_info_mbb before updating HDD.
     */
    if(HAL_STATUS_SUCCESS(status))
       return;

DEQ_PREAUTH:
    csr_roam_dequeue_preauth_reassoc(mac);
}

/**
 * csr_prepare_reassoc_req () - Prepares reassoc request
 * @pmac: MAC context
 * @session_id: session id
 * @pbss_description: bss description
 * @preassoc_req: pointer to reassociation request
 *
 *Return: None
 */
static void csr_prepare_reassoc_req(void* pmac, tANI_U32 session_id,
       void* pbss_description, void *preassoc_req)
{
    tDot11fBeaconIEs *ies_local = NULL;
    eHalStatus status;
    tpAniSirGlobal mac = (tpAniSirGlobal)pmac;
    tpSirBssDescription bss_description = (tpSirBssDescription)pbss_description;
    tSirSmeJoinReq **reassoc_req = (tSirSmeJoinReq **)preassoc_req;

    /* Get IE's */
    status = csrGetParsedBssDescriptionIEs(mac, bss_description, &ies_local);
    if (!HAL_STATUS_SUCCESS(status)) {
        smsLog(mac, LOGE,
               FL("csrGetParsedBssDescriptionIEs failed"));
        return;
    }
    if(ies_local == NULL) {
       smsLog(mac, LOGE,
              FL("ies_local is NULL"));
       return;
    }

    smsLog(mac, LOG1, FL("session_id %d"), session_id);

    status = csr_fill_reassoc_req(mac, session_id, bss_description,
                         ies_local, reassoc_req);
    if (!HAL_STATUS_SUCCESS(status)) {
        smsLog(mac, LOGE,
               FL("Reassociation request filling failed"));
        return;
    }
    vos_mem_free(ies_local);
    smsLog(mac, LOG1, FL("status %d"), status);
}

/**
 * csr_roaming_mbb_callback () - handles mbb callback
 * @pmac: MAC context
 * @session_id: session id
 * @pbss_description: bss description
 * @preassoc_req: pointer to reassociation request
 * @csr_roam_op_code: callback opcode
 *
 *Return: None
 */
static void csr_roaming_mbb_callback(void* pmac, tANI_U32 session_id,
       void* pbss_description, void *preassoc_req, tANI_U32 csr_roam_op_code)
{
    tpAniSirGlobal mac = (tpAniSirGlobal)pmac;

    smsLog(mac, LOG1,
           FL("csr_roam_op_code %d"), csr_roam_op_code);

    switch(csr_roam_op_code) {
    case SIR_ROAMING_DEREGISTER_STA:
         csrRoamCallCallback(mac, session_id, NULL, 0,
                              eCSR_ROAM_FT_START, eSIR_SME_SUCCESS);
        break;
    case SIR_STOP_ROAM_OFFLOAD_SCAN:
         csrRoamOffloadScan(mac, ROAM_SCAN_OFFLOAD_STOP, REASON_DISCONNECTED);
       break;
    case SIR_PREPARE_REASSOC_REQ:
         csr_prepare_reassoc_req(pmac, session_id, pbss_description,
                         preassoc_req);
         break;
    }
}

/**
 * csr_register_roaming_mbb_callback () - registers roaming callback
 * @mac: MAC context
 *
 *Return: eHAL_STATUS_SUCCESS on success, otherwise  failure
 */
eHalStatus csr_register_roaming_mbb_callback(tpAniSirGlobal mac)
{
    eHalStatus status;

    status = sme_AcquireGlobalLock(&mac->sme);
    if (eHAL_STATUS_SUCCESS == status) {
        mac->sme.roaming_mbb_callback = csr_roaming_mbb_callback;
        sme_ReleaseGlobalLock(&mac->sme);
    } else
        smsLog(mac, LOGE,
               FL("sme_AcquireGlobalLock error"));
    return status;
}
