Newer
Older
mbed-os / connectivity / FEATURE_BLE / cordio / TARGET_CORDIO_LL / stack / controller / sources / ble / lctr / lctr_main_init_master_ae.c
@Paul Szczeanek Paul Szczeanek on 7 Aug 2020 22 KB remove generic, TPPs, nested namespaces
/*************************************************************************************************/
/*!
 *  \file
 *
 *  \brief  Link layer controller master scanning operation builder implementation file.
 *
 *  Copyright (c) 2013-2019 Arm Ltd. All Rights Reserved.
 *
 *  Copyright (c) 2019-2020 Packetcraft, Inc.
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/*************************************************************************************************/

#include "lctr_int_init_master_ae.h"
#include "lctr_int.h"
#include "sch_api_ble.h"
#include "sch_api.h"
#include "wsf_assert.h"
#include "wsf_cs.h"
#include "wsf_math.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include <string.h>
#include <stddef.h>

/**************************************************************************************************
  Macros
**************************************************************************************************/
/*! \brief      Resolve the extended initiate handle from the context pointer. */
#define LCTR_GET_EXT_INIT_HANDLE(pCtx)  (pCtx - lctrMstExtInitTbl)

/*! \brief      Typical PDU length of ADV_EXT_IND when initiating connection. */
#define LCTR_INIT_ADV_EXT_IND_LEN       7       /*!< Includes Flags + ADI + AuxPtr. */

/*! \brief      Typical PDU length of AUX_ADV_IND when initiating connection. */
#define LCTR_INIT_AUX_ADV_IND_LEN       10      /*!< Includes Flags + AdvA + ADI. */

/*! \brief      Typical PDU length of ADV_IND when initiating connection with legacy packets. */
#define LCTR_INIT_ADV_IND_LEN           6

/**************************************************************************************************
  Globals
**************************************************************************************************/
/*! \brief      Initiate operational context. */
/*! \brief      Extended scan operational context. */
lctrExtScanCtx_t lctrMstExtInitTbl[LCTR_SCAN_PHY_TOTAL];

/*! \brief      Extended initiator control block. */
lctrExtInitCtrlBlk_t lctrMstExtInit;

/*************************************************************************************************/
/*!
 *  \brief      Master initiate reset handler.
 */
/*************************************************************************************************/
static void lctrMstExtInitResetHandler(void)
{
  LctrMstExtInitDefaults();
}

/*************************************************************************************************/
/*!
 *  \brief      Execute common master initiate state machine.
 *
 *  \param      pMsg    Pointer to message buffer.
 */
/*************************************************************************************************/
static void lctrMstExtInitExecuteCommonSm(LctrExtScanMsg_t *pMsg)
{
  /* Subsystem event handling. */
  switch (pMsg->hdr.event)
  {
    case LCTR_EXT_INIT_MSG_INITIATE:
    {
      LL_TRACE_INFO1("lctrMstExtInitExecuteCommonSm: scanMode=%u, event=INITIATE", lmgrCb.scanMode);
      lctrExtInitiateMsg_t *pExtInitMsg = (lctrExtInitiateMsg_t *)pLctrMsg;

      uint8_t status = 0;
      unsigned int i;

      /* Check worst case connSpec among all PHYs. */
      for (i = 0; i < LCTR_SCAN_PHY_TOTAL; i++)
      {
        if (lctrMstExtInit.enaPhys & (1 << i))
        {
          /* TODO: pre-check all connSpec with RM. */
        }
      }

      for (i = 0; i < LCTR_SCAN_PHY_TOTAL; i++)
      {
        if (lctrMstExtInit.enaPhys & (1 << i))
        {
          if ((status = lctrExtInitSetupInitiate(&lctrMstExtInitTbl[i], pExtInitMsg->peerAddrType, pExtInitMsg->peerAddr,
                                                 pExtInitMsg->filterPolicy, pExtInitMsg->ownAddrType)) != LL_SUCCESS)
          {
            /* TODO for multiple scanners, cleanup upon failure */
            break;
          }
        }
      }

      if (status != LL_SUCCESS)
      {
        break;
      }

      lctrMstExtInit.estConnPhys = 0;
      lctrMstExtInit.peerAddr = pExtInitMsg->peerAddr;
      lctrMstExtInit.peerAddrType = pExtInitMsg->peerAddrType;

      for (i = 0; i < LCTR_SCAN_PHY_TOTAL; i++)
      {
        if (lctrMstExtInit.enaPhys & (1 << i))
        {
          lctrMstConnBuildOp(LCTR_GET_CONN_CTX(lctrMstExtInitTbl[i].data.init.connHandle),
                             &lctrMstExtInitTbl[i].data.init.connInd);
          lctrMstExtInitiateOpCommit(&lctrMstExtInitTbl[i]);
        }
      }
      break;
    }

    case LCTR_EXT_INIT_MSG_INITIATE_CANCEL:
      LL_TRACE_INFO1("lctrMstExtInitExecuteCommonSm: scanMode=%u, event=INITIATE_CANCEL", lmgrCb.scanMode);
      break;

    case LCTR_EXT_INIT_MSG_RESET:
      LL_TRACE_INFO1("lctrMstExtInitExecuteCommonSm: scanMode=%u, event=RESET", lmgrCb.scanMode);
      break;

    case LCTR_EXT_INIT_MSG_TERMINATE:
      LL_TRACE_INFO1("lctrMstExtInitExecuteCommonSm: scanMode=%u, event=TERMINATE", lmgrCb.scanMode);
      break;

    default:
      LL_TRACE_ERR2("lctrMstExtInitExecuteCommonSm: scanMode=%u, event=%u -- unknown event", lmgrCb.scanMode, pMsg->hdr.event);
      /* No action required. */
      break;
  }
}

/*************************************************************************************************/
/*!
 *  \brief      Master initiate message dispatcher.
 *
 *  \param      pMsg    Pointer to message buffer.
 */
/*************************************************************************************************/
static void lctrMstExtInitDisp(LctrExtScanMsg_t *pMsg)
{
  lctrExtScanCtx_t *pExtInitCtx;
  uint8_t event = pMsg->hdr.event;

  bool_t isBcstMsg = FALSE;

  if (pMsg->hdr.dispId == LCTR_DISP_BCST)
  {
    /* Global broadcast message. */
    isBcstMsg = TRUE;
  }

  /* Broadcast message. */
  switch (event)
  {
    case LCTR_EXT_INIT_MSG_INITIATE:
    case LCTR_EXT_INIT_MSG_INITIATE_CANCEL:
      isBcstMsg = TRUE;
      break;
    default:
      break;
  }
  if (pMsg->hdr.handle == LCTR_SCAN_PHY_ALL)
  {
    isBcstMsg = TRUE;
  }

  /* Route message to SM. */
  if (!isBcstMsg)
  {
    WSF_ASSERT(pMsg->hdr.handle < LCTR_SCAN_PHY_TOTAL);
    if (lctrMstExtInit.enaPhys & (1 << pMsg->hdr.handle))
    {
      pExtInitCtx = &lctrMstExtInitTbl[pMsg->hdr.handle];
      lctrMstExtInitExecuteSm(pExtInitCtx, event);
    }
  }
  else
  {
    for (unsigned int i = 0; i < LCTR_SCAN_PHY_TOTAL; i++)
    {
      if (lctrMstExtInit.enaPhys & (1 << i))
      {
        pExtInitCtx = &lctrMstExtInitTbl[i];
        lctrMstExtInitExecuteSm(pExtInitCtx, event);
      }
    }
  }

  lctrMstExtInitExecuteCommonSm(pMsg);
}

/*************************************************************************************************/
/*!
 *  \brief  Build extended initiate operation.
 *
 *  \param  pExtInitCtx     Extended initiate context.
 *  \param  pConnSpec       Connection spec.
 *  \param  peerAddrType    Peer address type.
 *  \param  peerAddr        Peer address.
 *
 *  \return Status error code.
 */
/*************************************************************************************************/
uint8_t lctrMstExtInitiateBuildOp(lctrExtScanCtx_t *pExtInitCtx, LlConnSpec_t *pConnSpec, uint64_t peerAddr, uint8_t peerAddrType)
{
  BbOpDesc_t * const pOp = &pExtInitCtx->scanBod;
  BbBleData_t * const pBle = &pExtInitCtx->scanBleData;
  BbBleMstAdvEvent_t * const pScan = &pBle->op.mstAdv;

  memset(pOp, 0, sizeof(BbOpDesc_t));
  memset(pBle, 0, sizeof(BbBleData_t));

  /*** General Setup ***/

  pOp->reschPolicy = BB_RESCH_MOVEABLE;
  pOp->protId = BB_PROT_BLE;
  pOp->prot.pBle = pBle;
  pOp->endCback = lctrMstExtInitiateEndOp;
  pOp->abortCback = lctrMstExtInitiateEndOp;
  pOp->pCtx = pExtInitCtx;

  /*** BLE General Setup ***/

  pBle->chan.opType = BB_BLE_OP_MST_ADV_EVENT;

  pBle->chan.chanIdx = lctrScanChanSelectInit(lmgrMstScanCb.scanChanMap);
  pBle->chan.txPower = lmgrCb.advTxPwr;
  pBle->chan.accAddr = LL_ADV_ACCESS_ADDR;
  pBle->chan.crcInit = LL_ADV_CRC_INIT;
  switch (LCTR_GET_EXT_INIT_HANDLE(pExtInitCtx))
  {
    case LCTR_SCAN_PHY_1M:
    default:
      pBle->chan.txPhy = pBle->chan.rxPhy = BB_PHY_BLE_1M;
      break;
    case LCTR_SCAN_PHY_CODED:
      pBle->chan.txPhy = pBle->chan.rxPhy = BB_PHY_BLE_CODED;
      break;
  }

#if (LL_ENABLE_TESTER == TRUE)
  pBle->chan.accAddrRx = llTesterCb.advAccessAddrRx ^ pBle->chan.accAddr;
  pBle->chan.accAddrTx = llTesterCb.advAccessAddrTx ^ pBle->chan.accAddr;
  pBle->chan.crcInitRx = llTesterCb.advCrcInitRx ^ pBle->chan.crcInit;
  pBle->chan.crcInitTx = llTesterCb.advCrcInitTx ^ pBle->chan.crcInit;
#endif

  pBle->pduFilt.pduTypeFilt = (1 << LL_PDU_ADV_IND) |
                              (1 << LL_PDU_ADV_DIRECT_IND) |
                              (1 << LL_PDU_ADV_EXT_IND);
  if (pExtInitCtx->data.init.filtPolicy & LL_SCAN_FILTER_WL_BIT)
  {
    pBle->pduFilt.wlPduTypeFilt = pBle->pduFilt.pduTypeFilt;
  }

  /*** BLE Scan Setup: Rx advertising packet ***/

  pScan->scanChMap = lmgrMstScanCb.scanChanMap;

  pScan->rxAdvCback = lctrMstInitiateRxExtAdvPktHandler;
  pScan->rxAdvPostCback = lctrMstInitiateRxExtAdvPktPostProcessHandler;

  if ((pScan->pRxAdvBuf = WsfMsgAlloc(LL_ADV_HDR_LEN + LL_EXT_ADV_HDR_MAX_LEN)) == NULL)
  {
    LL_TRACE_ERR0("Could not allocate advertising buffer");
    return LL_ERROR_CODE_UNSPECIFIED_ERROR;
  }

  /*** BLE Scan Setup: Tx connect indication packet ***/

  pScan->txReqCback = lctrMstExtConnIndTxCompHandler;

  /* TODO move to common/shared init */
  lctrConnInd_t * const pConnInd = &pExtInitCtx->data.init.connInd;

  pConnInd->accessAddr = lctrComputeAccessAddr();
  pConnInd->crcInit = lctrComputeCrcInit();
  pConnInd->txWinSize = LL_MIN_TX_WIN_SIZE;             /* minimum size */
  /* pConnInd->txWinOffset = 0; */                      /* Updated in ISR immediately prior to Tx LL_CONN_IND. */
  pConnInd->interval = pExtInitCtx->data.init.connInterval;
  pConnInd->latency = pConnSpec->connLatency;
  pConnInd->timeout = pConnSpec->supTimeout;
  pConnInd->chanMask = lmgrCb.chanClass;
  pConnInd->hopInc = lctrComputeHopInc();
  pConnInd->masterSca = lctrComputeSca();

#if (LL_ENABLE_TESTER)
  if (llTesterCb.connIndEnabled)
  {
    memcpy(&pConnInd->accessAddr,
           &llTesterCb.connInd.accessAddr,
           sizeof(lctrConnInd_t) - offsetof(lctrConnInd_t, accessAddr));

    llTesterCb.connIndEnabled = FALSE;
  }
#endif

  pExtInitCtx->reqPduHdr.pduType = LL_PDU_CONNECT_IND;
  pExtInitCtx->reqPduHdr.len = LL_CONN_IND_PDU_LEN;

  /* Always match local address in PDU to initiator's address (in directed advertisements). */
  if (pExtInitCtx->data.init.ownAddrType & LL_ADDR_RANDOM_BIT)
  {
    WSF_ASSERT(lmgrCb.bdAddrRndValid);    /* No further verification after scan starts. */
    pBle->pduFilt.localAddrMatch = lmgrCb.bdAddrRnd;
    BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, LOCAL_ADDR_MATCH_RAND);
  }
  else
  {
    pBle->pduFilt.localAddrMatch = lmgrPersistCb.bdAddr;
  }
  BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, LOCAL_ADDR_MATCH_ENA);

  /* Potentially resolve peer & local addresses. */
  if (lmgrCb.addrResEna)
  {
    BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, PEER_ADDR_RES_ENA);
    BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, LOCAL_ADDR_RES_ENA);
  }

  /* Choose initiator's address. */
  pExtInitCtx->reqPduHdr.txAddrRnd = BB_BLE_PDU_FILT_FLAG_IS_SET(&pBle->pduFilt, LOCAL_ADDR_MATCH_RAND);
  pConnInd->initAddr = pBle->pduFilt.localAddrMatch;

  /* peerAddrType and pPeerAddr only valid when filter policy is set to none */
  if (pExtInitCtx->data.init.filtPolicy == LL_SCAN_FILTER_NONE)
  {
    /* Set advertiser's address. */
    pExtInitCtx->reqPduHdr.rxAddrRnd = peerAddrType & LL_ADDR_RANDOM_BIT;
    pConnInd->advAddr = peerAddr;

    pBle->pduFilt.peerAddrMatch = peerAddr;
    if (peerAddrType & LL_ADDR_RANDOM_BIT)
    {
      BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, PEER_ADDR_MATCH_RAND);
    }
    BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, PEER_ADDR_MATCH_ENA);
  }

  uint8_t *pBuf;
  pBuf  = pExtInitCtx->reqBuf;
  pBuf += lctrPackAdvbPduHdr(pBuf, &pExtInitCtx->reqPduHdr);
  /* pBuf += */ lctrPackConnIndPdu(pBuf, pConnInd);

  pScan->pTxReqBuf = pExtInitCtx->reqBuf;
  pScan->txReqLen = LL_ADV_HDR_LEN + LL_CONN_IND_PDU_LEN;

  /*** Commit operation ***/

  /* Postponed in lctrMstExtInitiateOpCommit() until after connBod is built. */

  return LL_SUCCESS;
}

/*************************************************************************************************/
/*!
 *  \brief  Commit initiate connection operation.
 *
 *  \param  pExtInitCtx     Extended scan context of the initiator.
 */
/*************************************************************************************************/
void lctrMstExtInitiateOpCommit(lctrExtScanCtx_t *pExtInitCtx)
{
  BbOpDesc_t * const pOp = &pExtInitCtx->scanBod;

  /*** Commit operation ***/

  /* Postponed in lctrMstInitiateOpCommit() until after connBod is built. */

  pOp->minDurUsec = LCTR_MIN_SCAN_USEC;
  pOp->maxDurUsec = LCTR_BLE_TO_US(pExtInitCtx->data.init.param.scanWindow);

  pExtInitCtx->selfTerm = FALSE;
  pExtInitCtx->shutdown = FALSE;

  SchInsertNextAvailable(pOp);
  pExtInitCtx->data.init.scanWinStartUsec = pOp->dueUsec;
}

/*************************************************************************************************/
/*!
 *  \brief  Build auxiliary scan discovery operation.
 *
 *  \param  pExtInitCtx     Extended scan context of the initiator.
 *  \param  pConnSpec       Connection spec.
 *  \param  peerAddrType    Peer address type.
 *  \param  peerAddr        Peer address.
 *
 *  \return Status error code.
 */
/*************************************************************************************************/
uint8_t lctrMstAuxInitiateBuildOp(lctrExtScanCtx_t *pExtInitCtx, LlConnSpec_t *pConnSpec, uint64_t peerAddr, uint8_t peerAddrType)
{
  /* Pre-resolve common structures for efficient access. */
  BbOpDesc_t * const pOp = &pExtInitCtx->auxScanBod;
  BbBleData_t * const pBle = &pExtInitCtx->auxBleData;
  BbBleMstAuxAdvEvent_t * const pAuxScan = &pBle->op.mstAuxAdv;

  memset(pOp, 0, sizeof(BbOpDesc_t));
  memset(pBle, 0, sizeof(BbBleData_t));

  /*** General Setup ***/

  pOp->reschPolicy = BB_RESCH_MOVEABLE_PREFERRED;
  pOp->protId = BB_PROT_BLE;
  pOp->prot.pBle = pBle;
  pOp->endCback = lctrMstAuxInitiateEndOp;
  pOp->abortCback = lctrMstAuxInitiateEndOp;
  pOp->pCtx = pExtInitCtx;

  /*** BLE General Setup ***/

  pBle->chan.opType = BB_BLE_OP_MST_AUX_ADV_EVENT;

  /* pBle->chan.chanIdx = 0; */     /* write after ADV_EXT_IND is received */
  pBle->chan.txPower = lmgrCb.advTxPwr;
  pBle->chan.accAddr = LL_ADV_ACCESS_ADDR;
  pBle->chan.crcInit = LL_ADV_CRC_INIT;
  /* pBle->chan.txPhy = 0; */       /* write after ADV_EXT_IND is received */
  /* pBle->chan.rxPhy = 0; */       /* write after ADV_EXT_IND is received */
  /* pBle->chan.phyOptions = 0; */  /* write after ADV_EXT_IND is received */

#if (LL_ENABLE_TESTER == TRUE)
  pBle->chan.accAddrRx = llTesterCb.advAccessAddrRx ^ pBle->chan.accAddr;
  pBle->chan.accAddrTx = llTesterCb.advAccessAddrTx ^ pBle->chan.accAddr;
  pBle->chan.crcInitRx = llTesterCb.advCrcInitRx ^ pBle->chan.crcInit;
  pBle->chan.crcInitTx = llTesterCb.advCrcInitTx ^ pBle->chan.crcInit;
#endif

  pBle->pduFilt.pduTypeFilt = (1 << LL_PDU_AUX_ADV_IND) |
                              (1 << LL_PDU_AUX_CONNECT_RSP);
  if (pExtInitCtx->data.init.filtPolicy & LL_SCAN_FILTER_WL_BIT)
  {
    pBle->pduFilt.wlPduTypeFilt = pBle->pduFilt.pduTypeFilt;
  }

  /*** BLE Scan Setup: Rx packets ***/
  pAuxScan->isInit = TRUE;
  pAuxScan->rxAuxAdvCback = lctrMstInitiateRxAuxAdvPktHandler;

  /*** BLE Scan Setup: Tx connect request packet ***/

  /* Always match local address in PDU to initiator's address (in directed advertisements). */
  if (pExtInitCtx->data.init.ownAddrType & LL_ADDR_RANDOM_BIT)
  {
    WSF_ASSERT(lmgrCb.bdAddrRndValid);    /* No further verification after scan starts. */
    pBle->pduFilt.localAddrMatch = lmgrCb.bdAddrRnd;
    BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, LOCAL_ADDR_MATCH_RAND);
  }
  else
  {
    pBle->pduFilt.localAddrMatch = lmgrPersistCb.bdAddr;
  }
  BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, LOCAL_ADDR_MATCH_ENA);

  /* Potentially resolve peer & local addresses. */
  if (lmgrCb.addrResEna)
  {
    BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, PEER_ADDR_RES_ENA);
    BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, LOCAL_ADDR_RES_ENA);
  }

  /* peerAddrType and pPeerAddr only valid when filter policy is set to none */
  if (pExtInitCtx->data.init.filtPolicy == LL_SCAN_FILTER_NONE)
  {
    pBle->pduFilt.peerAddrMatch = peerAddr;
    if (peerAddrType & LL_ADDR_RANDOM_BIT)
    {
      BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, PEER_ADDR_MATCH_RAND);
    }
    BB_BLE_PDU_FILT_SET_FLAG(&pBle->pduFilt, PEER_ADDR_MATCH_ENA);
  }

  /* Use primary channel's CONNECT_REQ PDU. */
  pAuxScan->pTxAuxReqBuf = pExtInitCtx->reqBuf;
  pAuxScan->txAuxReqLen = LL_ADV_HDR_LEN + LL_CONN_IND_PDU_LEN;

#if (LL_ENABLE_TESTER)
  if (llTesterCb.auxReq.len)
  {
    memcpy(pAuxScan->pTxAuxReqBuf, llTesterCb.auxReq.buf, llTesterCb.auxReq.len);
    pAuxScan->txAuxReqLen = llTesterCb.auxReq.len;
  }
#endif

  /*** BLE Scan Setup: Rx auxiliary connection response packet ***/

  pAuxScan->rxAuxRspCback = lctrMstInitiateRxAuxConnRspHandler;

  /*** BLE Scan Setup: Rx chain packet ***/

  /* pAuxScan->rxAuxChainCback = NULL; */    /* No CHAIN_IND when inititating. */

  /*** Commit operation ***/

  /* pOp->minDurUsec = 0; */  /* Defer assignment until AuxPtr is received. */
  /* pOp->maxDurUsec = 0; */  /* Not used for aux scan. */

  pExtInitCtx->selfTerm = FALSE;
  pExtInitCtx->shutdown = FALSE;
  pExtInitCtx->auxOpPending = FALSE;

  /* Defer scheduling until AuxPtr is received. */

  return LL_SUCCESS;
}

/*************************************************************************************************/
/*!
 *  \brief      Initialize link layer controller resources for initiating master.
 */
/*************************************************************************************************/
void LctrMstExtInitInit(void)
{
  /* Add initiate reset handler. */
  lctrResetHdlrTbl[LCTR_DISP_INIT] = lctrMstExtInitResetHandler;

  /* Add initiate message dispatchers. */
  lctrMsgDispTbl[LCTR_DISP_EXT_INIT] = (LctrMsgDisp_t)lctrMstExtInitDisp;


  /* Add utility function pointers. */
  LctrMstExtInitEnabled = LctrMstExtInitIsEnabled;

  LctrMstExtInitDefaults();

  lmgrPersistCb.extInitCtxSize = sizeof(lctrExtScanCtx_t);
}

/*************************************************************************************************/
/*!
 *  \brief      Set default values for scanning master.
 */
/*************************************************************************************************/
void LctrMstExtInitDefaults(void)
{
  memset(&lctrMstExtInitTbl, 0, sizeof(lctrMstExtInitTbl));
  memset(&lctrMstExtInit, 0, sizeof(lctrMstExtInit));

  lmgrCb.numExtScanPhys = 1;
  lctrMstExtInit.enaPhys = 1 << LCTR_SCAN_PHY_1M;
}

/*************************************************************************************************/
/*!
 *  \brief      Set enabled initiate scanning PHY.
 *
 *  \param      scanPhy     Enabled scanning PHY.
 */
/*************************************************************************************************/
void LctrMstExtInitSetScanPhy(uint8_t scanPhy)
{
  WSF_ASSERT(scanPhy < LCTR_SCAN_PHY_TOTAL);

  lctrMstExtInit.enaPhys |= 1 << scanPhy;
}

/*************************************************************************************************/
/*!
 *  \brief      Clear (disable) scanning PHY.
 *
 *  \param      scanPhy     Disabled scanning PHY.
 */
/*************************************************************************************************/
void LctrMstExtInitClearScanPhy(uint8_t scanPhy)
{
  WSF_ASSERT(scanPhy < LCTR_SCAN_PHY_TOTAL);

  lctrMstExtInit.enaPhys &= ~(1 << scanPhy);
}

/*************************************************************************************************/
/*!
 *  \brief      Set extended initiate parameters.
 *
 *  \param      initPhy     Extended initiating PHY.
 *  \param      pScanParam  Extended initiating scan parameters.
 *  \param      pConnSpec   Connection specification.
 */
/*************************************************************************************************/
void LctrMstExtInitParam(uint8_t initPhy, const LlExtInitScanParam_t *pScanParam, const LlConnSpec_t *pConnSpec)
{
  WSF_ASSERT(initPhy < LCTR_SCAN_PHY_TOTAL);

  if (pScanParam)
  {
    lctrMstExtInitTbl[initPhy].data.init.param = *pScanParam;
  }
  lctrMstExtInitTbl[initPhy].data.init.connSpec = *pConnSpec;
}

/*************************************************************************************************/
/*!
 *  \brief  Send internal extended initiate subsystem message.
 *
 *  \param  pExtInitCtx Extended initiate context.
 *  \param  event       Extended scan event.
 */
/*************************************************************************************************/
void lctrSendExtInitMsg(lctrExtScanCtx_t *pExtInitCtx, uint8_t event)
{
  lctrMsgHdr_t *pMsg;

  if ((pMsg = WsfMsgAlloc(sizeof(lctrMsgHdr_t))) != NULL)
  {
    pMsg->handle = (pExtInitCtx) ? LCTR_GET_EXT_INIT_HANDLE(pExtInitCtx) : LCTR_SCAN_PHY_ALL;
    pMsg->dispId = LCTR_DISP_EXT_INIT;
    pMsg->event = event;

    WsfMsgSend(lmgrPersistCb.handlerId, pMsg);
  }
}

/*************************************************************************************************/
/*!
 *  \brief      Check if scanner is enabled or not.
 *
 *  \param      scanPhy   scanner Phy.
 *
 *  \return     True if scanner enabled. False if not.
 */
/*************************************************************************************************/
bool_t LctrMstExtInitIsEnabled(uint8_t scanPhy)
{
  return (lctrMstExtInitTbl[scanPhy].state != LCTR_EXT_SCAN_STATE_DISABLED);
}

/*************************************************************************************************/
/*!
 *  \brief      Check if private addresses are being used.
 *
 *  \param      scanPhy   scanner Phy.
 *
 *  \return     Returns True if scanner is using private addresses. False if not.
 */
/*************************************************************************************************/
bool_t LctrMstExtInitIsPrivAddr(uint8_t scanPhy)
{
  /* Check if private address bit is enabled. */
  return (lctrMstExtInitTbl[scanPhy].scanParam.ownAddrType & LL_ADDR_RANDOM_BIT);
}