Newer
Older
mbed-os / connectivity / FEATURE_BLE / cordio / TARGET_CORDIO_LL / stack / controller / sources / ble / lctr / lctr_main_conn_slave.c
@Paul Szczeanek Paul Szczeanek on 7 Aug 2020 15 KB remove generic, TPPs, nested namespaces
/*************************************************************************************************/
/*!
 *  \file
 *
 *  \brief  Link layer controller slave connection 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_conn.h"
#include "lctr_int_conn_slave.h"
#include "lctr_int_adv_slave.h"
#include "lmgr_api_conn.h"
#include "sch_api.h"
#include "sch_api_ble.h"
#include "bb_ble_api.h"
#include "wsf_assert.h"
#include "wsf_math.h"
#include "wsf_msg.h"
#include "wsf_os.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include <string.h>

/**************************************************************************************************
  Global Variables
**************************************************************************************************/

/*! \brief      SCA PPM table. */
const uint16_t scaPpmTbl[] =
{
  500,    /*!< LL_MCA_500_PPM */
  250,    /*!< LL_MCA_250_PPM */
  150,    /*!< LL_MCA_150_PPM */
  100,    /*!< LL_MCA_100_PPM */
  75,     /*!< LL_MCA_75_PPM */
  50,     /*!< LL_MCA_50_PPM */
  30,     /*!< LL_MCA_30_PPM */
  20      /*!< LL_MCA_20_PPM */
};

/*************************************************************************************************/
/*!
 *  \brief      Process a received connection indication PDU in slave role.
 *
 *  \param      pMsg    Pointer to message buffer.
 */
/*************************************************************************************************/
static void lctrSlvProcessConnInd(lctrConnMsg_t *pMsg)
{
  lctrConnEstablish_t *pConnEst = &pMsg->connEstablish;
  lctrConnCtx_t *pCtx;

  if ((pCtx = lctrAllocConnCtx()) != NULL)
  {
    pLctrConnMsg = pMsg;
    pCtx->role = LL_ROLE_SLAVE;
    lctrSlvConnExecuteSm(pCtx, LCTR_CONN_MSG_RX_CONNECT_IND);
  }
  else
  {
    lctrNotifyHostConnectInd(0, LL_ROLE_SLAVE, &pConnEst->connInd, pConnEst->peerIdAddrType,
                             pConnEst->peerIdAddr, pConnEst->peerRpa, pConnEst->localRpa,
                             LL_ERROR_CODE_CONN_LIMIT_EXCEEDED, 0);
  }

  /* Advertising has stopped. */
  BbStop(BB_PROT_BLE);
  if (pConnEst->sendAdvSetTerm)
  {
    LmgrSendAdvSetTermInd(pConnEst->hdr.handle, LL_SUCCESS, LCTR_GET_CONN_HANDLE(pCtx), pConnEst->numExtAdvEvents);
  }
}

/*************************************************************************************************/
/*!
 *  \brief      Process a received data channel PDU in slave role.
 *
 *  \param      pCtx    Connection context.
 *  \param      pBuf    PDU buffer.
 */
/*************************************************************************************************/
static void lctrSlvProcessDataPdu(lctrConnCtx_t *pCtx, uint8_t *pBuf)
{
  uint8_t result;

  if (pCtx->enabled)
  {
    result = lctrDecodeCtrlPdu(&lctrDataPdu, pBuf, pCtx->role);

    switch (result)
    {
      case LL_SUCCESS:
#if (LL_ENABLE_TESTER)
        if ((llTesterCb.rxLlcpFilter & (1 << lctrDataPdu.opcode)) != 0)
        {
          return;
        }
#endif
        lctrSlvConnExecuteSm(pCtx, LCTR_CONN_MSG_RX_LLCP);
        break;
      case LL_ERROR_CODE_INVALID_LMP_PARAMS:
        lctrSlvConnExecuteSm(pCtx, LCTR_CONN_MSG_RX_LLCP_INVALID_PARAM);
        break;
      default:
        lctrSlvConnExecuteSm(pCtx, LCTR_CONN_MSG_RX_LLCP_UNKNOWN);
        break;
    }
  }
}

/*************************************************************************************************/
/*!
 *  \brief      Slave connection reset handler.
 */
/*************************************************************************************************/
static void lctrSlvConnResetHandler(void)
{
  BbBleConnSlaveInit();
  SchRmInit();
  SchTmInit();
  lctrConnDefaults();
  LmgrConnInit();
}

/*************************************************************************************************/
/*!
 *  \brief      Execute slave state machine.
 *
 *  \param      pMsg    Pointer to message buffer.
 */
/*************************************************************************************************/
static void lctrSlvConnExecute(lctrConnMsg_t *pMsg)
{
  lctrConnCtx_t *pCtx;
  pCtx = LCTR_GET_CONN_CTX(pMsg->hdr.handle);

  if (pCtx->enabled)
  {
    pLctrConnMsg = pMsg;

    lctrSlvConnExecuteSm(pCtx, pMsg->hdr.event);
  }
}

/*************************************************************************************************/
/*!
 *  \brief      Slave connection message dispatcher.
 *
 *  \param      pMsg    Pointer to message buffer.
 */
/*************************************************************************************************/
static void lctrSlvConnDisp(lctrConnMsg_t *pMsg)
{
  if (pMsg->hdr.dispId != LCTR_DISP_BCST)
  {
    pLctrConnMsg = pMsg;

    WSF_ASSERT(pMsg->hdr.handle < pLctrRtCfg->maxConn);
    lctrSlvConnExecute(pMsg);
  }
  else
  {
    for (pMsg->hdr.handle = 0; pMsg->hdr.handle < pLctrRtCfg->maxConn; pMsg->hdr.handle++)
    {
      pLctrConnMsg = pMsg;

      lctrSlvConnExecute(pMsg);
    }
  }
}

/*************************************************************************************************/
/*!
 *  \brief      Get reference time(due time) of the slave connection handle.
 *
 *  \param      connHandle    Connection handle.
 *
 *  \return     Due time in microseconds of the connection handle.
 */
/*************************************************************************************************/
static uint32_t lctrGetSlvConnRefTime(uint8_t connHandle)
{
  uint32_t refTime = 0;
  lctrConnCtx_t *pCtx = LCTR_GET_CONN_CTX(connHandle);

  if (pCtx->enabled && (pCtx->bleData.chan.opType == BB_BLE_OP_SLV_CONN_EVENT))
  {
    refTime = pCtx->connBod.dueUsec;
  }

  return refTime;
}

/*************************************************************************************************/
/*!
 *  \brief  Compute the total clock accuracy.
 *
 *  \param  mstScaIdx   Master sleep clock accuracy index.
 *
 *  \return Combined sleep clock inaccuracy.
 */
/*************************************************************************************************/
uint16_t lctrCalcTotalAccuracy(uint8_t mstScaIdx)
{
  const uint16_t clkPpm = BbGetClockAccuracy();

  if (mstScaIdx >= (sizeof(scaPpmTbl) / sizeof(scaPpmTbl[0])))
  {
    /* Cap to highest index. */
    mstScaIdx = LL_MCA_20_PPM;
  }

  return scaPpmTbl[mstScaIdx] + clkPpm;
}

/*************************************************************************************************/
/*!
 *  \brief  Build a connection operation.
 *
 *  \param  pCtx        Connection context.
 *
 *  This routine is called in response to a received CONNECTION_REQ PDU. The PDU must already
 *  be decoded in the lctrPduSlvAdvb variable prior to this call.
 */
/*************************************************************************************************/
void lctrSlvConnBuildOp(lctrConnCtx_t *pCtx)
{
  /* Pre-resolve common structures for efficient access. */
  BbOpDesc_t * const pOp = &pCtx->connBod;
  BbBleData_t * const pBle = &pCtx->bleData;
  BbBleSlvConnEvent_t * const pConn = &pBle->op.slvConn;
  lctrConnInd_t * const pConnInd = &pLctrConnMsg->connEstablish.connInd;

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

  /*** Connection context setup ***/
  uint16_t  txWinDelay;
  if (pLctrConnMsg->connEstablish.isAuxConnReq)
  {
    switch (pLctrConnMsg->connEstablish.phy)
    {
      case BB_PHY_BLE_1M:
      case BB_PHY_BLE_2M:
      default:
        txWinDelay = LCTR_DATA_CHAN_DLY_AUX_UNCODED;
        break;
      case BB_PHY_BLE_CODED:
        txWinDelay = LCTR_DATA_CHAN_DLY_AUX_CODED;
        break;
    }
  }
  else
  {
    txWinDelay = LCTR_DATA_CHAN_DLY;
  }

  const uint16_t txWinOffsetCnt = pConnInd->txWinOffset + txWinDelay;

  /* pCtx->lastChanIdx = 0; */              /* cleared in alloc */
  /* pCtx->eventCounter = 0; */             /* cleared in alloc */
  pCtx->data.slv.lastActiveEvent = 1;       /* guarantee execution of first connection event. */

  pCtx->usedChSel = pLctrConnMsg->connEstablish.usedChSel;

  pCtx->chanMask = pConnInd->chanMask;
  pCtx->hopInc = pConnInd->hopInc;
  pCtx->connInterval = pConnInd->interval;
  /* pCtx->txHdr.sn = 0; */                 /* cleared in alloc */
  /* pCtx->txHdr.nesn = 0; */               /* cleared in alloc */
  pCtx->data.slv.totalAcc = lctrCalcTotalAccuracy(pConnInd->masterSca);
  pCtx->maxLatency = pConnInd->latency;
  pCtx->supTimeoutMs = LCTR_CONN_IND_TO_MS(pConnInd->timeout);
  pCtx->authTimeoutMs = LL_DEF_AUTH_TO_MS;
  pCtx->pingPeriodMs = lctrCalcPingPeriodMs(pCtx, LL_DEF_AUTH_TO_MS);
  /* pCtx->llcpState = LCTR_LLCP_STATE_IDLE; */
  pCtx->llcpActiveProc = LCTR_PROC_INVALID;
  pCtx->crcInit = pConnInd->crcInit;

  /* Initially use fast termination. */
  uint32_t fastTermCnt = txWinOffsetCnt + pConnInd->txWinSize +
                         (LCTR_FAST_TERM_CNT * pConnInd->interval);
  WsfTimerStartMs(&pCtx->tmrSupTimeout, LCTR_CONN_IND_MS(fastTermCnt));

  lctrBuildRemapTable(pCtx);
  pCtx->chIdentifier = (pConnInd->accessAddr >> 16) ^
                       (pConnInd->accessAddr >> 0);

  /*** BLE general setup ***/

  pBle->chan.opType = BB_BLE_OP_SLV_CONN_EVENT;

  /* pBle->chan.chanIdx = 0; */             /* deferred assignment */
  pBle->chan.txPower = pLctrRtCfg->defTxPwrLvl;
  pBle->chan.accAddr = pConnInd->accessAddr;
  pBle->chan.crcInit = pConnInd->crcInit;
  pBle->chan.txPhy = pBle->chan.rxPhy = pLctrConnMsg->connEstablish.phy;
  lctrInitPhyTxPower(pCtx);

#if (LL_ENABLE_TESTER)
  pBle->chan.accAddrRx = pConnInd->accessAddr ^ llTesterCb.dataAccessAddrRx;
  pBle->chan.accAddrTx = pConnInd->accessAddr ^ llTesterCb.dataAccessAddrTx;
  pBle->chan.crcInitRx = pConnInd->crcInit    ^ llTesterCb.dataCrcInitRx;
  pBle->chan.crcInitTx = pConnInd->crcInit    ^ llTesterCb.dataCrcInitTx;
#endif

  /* pBle->chan.enc.enaEncrypt = FALSE; */  /* cleared in alloc */
  /* pBle->chan.enc.enaDecrypt = FALSE; */
  pBle->chan.enc.enaAuth = TRUE;
  /* pBle->chan.enc.nonceMode = PAL_BB_NONCE_MODE_PKT_CNTR; */  /* cleared in alloc */

  pCtx->txHdr.llid = ~LL_LLID_VS_PDU;     /* reset last PDU LLID */

  pCtx->effConnDurUsec = lctrCalcConnDurationUsec(pCtx, &pCtx->effDataPdu);      /* actual PHY */
  pCtx->localConnDurUsec = lctrCalcConnDurationUsec(pCtx, &pCtx->localDataPdu);  /* actual PHY */

   /*** General setup ***/

  const uint32_t txWinOffsetUsec = LCTR_CONN_IND_US(txWinOffsetCnt);
  const uint32_t txWinSizeUsec   = LCTR_CONN_IND_US(pConnInd->txWinSize);
  const uint32_t wwOffsetUsec    = lctrCalcWindowWideningUsec((txWinOffsetUsec + txWinSizeUsec), pCtx->data.slv.totalAcc);

  pCtx->data.slv.anchorPointUsec = pLctrConnMsg->connEstablish.connIndEndTsUsec + txWinOffsetUsec;    /* estimated initial anchor point */
  pCtx->data.slv.txWinSizeUsec = txWinSizeUsec;

  pOp->dueUsec = pCtx->data.slv.anchorPointUsec - wwOffsetUsec;
  pOp->minDurUsec = txWinSizeUsec + pCtx->effConnDurUsec + (wwOffsetUsec << 1);
  /* pOp->maxDurUsec = 0; */                  /* cleared in alloc */

  pOp->reschPolicy = BB_RESCH_FIXED;
  pOp->protId = BB_PROT_BLE;
  pOp->prot.pBle = pBle;
  pOp->endCback = lctrSlvConnEndOp;
  pOp->abortCback = lctrSlvConnAbortOp;
  pOp->pCtx = pCtx;

  /*** BLE connection setup ***/

  pConn->rxSyncDelayUsec = txWinSizeUsec + (wwOffsetUsec << 1) +
                           1;    /* rounding compensation when computing reqEndTs */
  pConn->execCback   = lctrSlvConnBeginOp;
  pConn->cancelCback = lctrSlvConnCleanupOp;
  pConn->txDataCback = lctrSlvConnTxCompletion;
  pConn->rxDataCback = lctrSlvConnRxCompletion;

  /*** Commit operation ***/

  const uint32_t ceDurUsec = pOp->minDurUsec;
  const uint32_t ceSyncDlyUsec = pConn->rxSyncDelayUsec;

  while (TRUE)
  {
    pBle->chan.chanIdx = lctrChSelHdlr[pCtx->usedChSel](pCtx, 0);

    if (SchInsertAtDueTime(pOp, lctrConnResolveConflict))
    {
      LL_TRACE_INFO1("    >>> Connection established, handle=%u <<<", LCTR_GET_CONN_HANDLE(pCtx));
      LL_TRACE_INFO1("                                connIntervalUsec=%u", LCTR_CONN_IND_US(pCtx->connInterval));
      LL_TRACE_INFO1("                                dueUsec=%u", pOp->dueUsec);
      LL_TRACE_INFO1("                                minDurUsec=%u", pOp->minDurUsec);
      break;
    }

    LL_TRACE_WARN1("!!! Establish CE schedule conflict handle=%u", LCTR_GET_CONN_HANDLE(pCtx));

    pCtx->eventCounter += 1;

    /* Initial eventCounter starts at 0; equivalent to unsynchronized intervals. */
    uint32_t unsyncTimeUsec = LCTR_CONN_IND_US(pCtx->connInterval * pCtx->eventCounter);
    uint32_t wwTotalUsec    = lctrCalcWindowWideningUsec(unsyncTimeUsec, pCtx->data.slv.totalAcc);

    /* Advance to next interval. */
    pOp->dueUsec = pCtx->data.slv.anchorPointUsec + unsyncTimeUsec - wwTotalUsec;

    pOp->minDurUsec = ceDurUsec + wwTotalUsec;
    pConn->rxSyncDelayUsec = ceSyncDlyUsec + (wwTotalUsec << 1);
  }

  /* Update topology manager information. */
  SchTmAdd(LCTR_GET_CONN_HANDLE(pCtx), LCTR_CONN_IND_US(pCtx->connInterval), pCtx->effConnDurUsec, TRUE, lctrGetSlvConnRefTime);
}

/*************************************************************************************************/
/*!
 *  \brief      Initialize link layer controller resources for connectable slave.
 */
/*************************************************************************************************/
void LctrSlvConnInit(void)
{
  /* Add reset handler (values set by master prevails). */
  lctrResetHdlrTbl[LCTR_DISP_CONN] = lctrSlvConnResetHandler;

  /* Add connection message dispatchers (values set by master prevails). */
  lctrMsgDispTbl[LCTR_DISP_CONN_IND] = (LctrMsgDisp_t)lctrSlvProcessConnInd;
  if (!lctrMsgDispTbl[LCTR_DISP_CONN])
  {
    lctrMsgDispTbl[LCTR_DISP_CONN] = (LctrMsgDisp_t)lctrSlvConnDisp;
  }

  /* Add connection event handlers. */
  lctrEventHdlrTbl[LCTR_EVENT_RX_PENDING]  = lctrConnRxPendingHandler;
  /* lctrEventHdlrTbl[LCTR_EVENT_TX_PENDING]  = lctrSlvConnTxPendingHandler; */     /* not used by slave */
  lctrEventHdlrTbl[LCTR_EVENT_TX_COMPLETE] = lctrConnTxCompletedHandler;

  /* Add LLCP SM handler. */
  lctrSlvLlcpSmTbl[LCTR_LLCP_SM_CONN_UPD] = lctrSlvLlcpExecuteConnUpdSm;
  lctrSlvLlcpSmTbl[LCTR_LLCP_SM_CMN]      = lctrLlcpExecuteCommonSm;

  /* Add control PDU handler. */
  if (!lctrCtrlPduHdlr)
  {
    lctrCtrlPduHdlr = lctrSlvProcessDataPdu;
  }

  /* Add channel selection handler. */
  lctrChSelHdlr[LL_CH_SEL_1] = lctrSelectNextDataChannel;

  lctrConnDefaults();

  /* Set supported features. */
  if (pLctrRtCfg->btVer >= LL_VER_BT_CORE_SPEC_4_1)
  {
    lmgrPersistCb.featuresDefault |=
      LL_FEAT_CONN_PARAM_REQ_PROC |
      LL_FEAT_EXT_REJECT_IND |
      LL_FEAT_SLV_INIT_FEAT_EXCH;
  }
  if (pLctrRtCfg->btVer >= LL_VER_BT_CORE_SPEC_4_2)
  {
    lmgrPersistCb.featuresDefault |=
      LL_FEAT_DATA_LEN_EXT;
  }
  if (pLctrRtCfg->btVer >= LL_VER_BT_CORE_SPEC_5_0)
  {
    lmgrPersistCb.featuresDefault |=
      LL_FEAT_MIN_NUM_USED_CHAN;
  }
  if (pLctrRtCfg->btVer >= LL_VER_BT_CORE_SPEC_5_1)
  {
    lmgrPersistCb.featuresDefault |=
        (LL_FEAT_PAST_SENDER | LL_FEAT_SCA_UPDATE);
  }
}

/*************************************************************************************************/
/*!
 *  \brief      Set default values for connection.
 */
/*************************************************************************************************/
void lctrConnDefaults(void)
{
  memset(pLctrConnTbl, 0, (sizeof(lctrConnCtx_t) * pLctrRtCfg->maxConn));
}