Newer
Older
mbed-os / connectivity / FEATURE_BLE / cordio / TARGET_CORDIO_LL / stack / controller / sources / ble / bb / bb_ble_adv_master.c
@Paul Szczeanek Paul Szczeanek on 7 Aug 2020 16 KB remove generic, TPPs, nested namespaces
/*************************************************************************************************/
/*!
 *  \file
 *
 *  \brief      Scanning master BLE baseband porting 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 "bb_api.h"
#include "pal_bb.h"
#include "bb_ble_int.h"
#include "sch_api.h"
#include "wsf_math.h"
#include <string.h>
#include "bb_ble_sniffer_api.h"

/**************************************************************************************************
  Macros
**************************************************************************************************/

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

BbBleScanPktStats_t bbScanStats;          /*!< Scan packet statistics. */

extern const BbRtCfg_t *pBbRtCfg;

/*************************************************************************************************/
/*!
 *  \brief      Calculate remaining scan duration.
 *
 *  \param      pBod        Pointer to the BOD.
 *  \param      pScan       Pointer to the scan event operation data.
 *  \param      refTime     Reference time.
 *  \param      setupUsec   Setup delay required in microseconds.
 *
 *  \return     Scan duration in microseconds.
 *
 *  This function is calculates the duration of the scan examining:
 *      - total elapsed scan time
 *      - neighboring BOD
 *      - maximum scan period
 */
/*************************************************************************************************/
static uint32_t bbBleCalcScanDurationUsec(BbOpDesc_t *pBod, BbBleMstAdvEvent_t *pScan, uint32_t refTime, uint32_t setupUsec)
{
  BbBleData_t * const pBle = pBod->prot.pBle;

  WSF_ASSERT(pBod->maxDurUsec > 0);

  /* Guard time at the end of a scan window to the next BOD. */
  /* Backoff for RX of preamble and AA which are compensated in BB driver. */
  uint32_t totalGapUsec = BbGetSchSetupDelayUs() + setupUsec;

  switch (pBle->chan.rxPhy)
  {
    case BB_PHY_BLE_1M:
      totalGapUsec += (LL_BLE_US_PER_BYTE_1M * LL_PREAMBLE_LEN_1M) + \
                      (LL_BLE_US_PER_BYTE_1M * LL_AA_LEN);
      break;
    case BB_PHY_BLE_2M:
      totalGapUsec += (LL_BLE_US_PER_BYTE_2M * LL_PREAMBLE_LEN_2M) + \
                      (LL_BLE_US_PER_BYTE_2M * LL_AA_LEN);
      break;
    case BB_PHY_BLE_CODED:
      totalGapUsec += (LL_BLE_US_PER_BIT_CODED_S8 * LL_PREAMBLE_LEN_CODED_BITS) + \
                      (LL_BLE_US_PER_BYTE_CODED_S8 * LL_AA_LEN);
      break;
    default:
      break;
  }

  if ((pScan->elapsedUsec + totalGapUsec) > pBod->maxDurUsec)
  {
    /* Scan window completed. */
    pScan->elapsedUsec = pBod->maxDurUsec;
    return 0;
  }

  uint32_t remTimeUsec = pBod->maxDurUsec - pScan->elapsedUsec;

  if (pBod->pNext)
  {

    uint32_t timeToNextOpUsec = BbGetTargetTimeDelta(pBod->pNext->dueUsec, refTime);

    /* Limit scanning to the edge of neighboring BOD. */
    remTimeUsec = WSF_MIN(remTimeUsec, timeToNextOpUsec);

    if (remTimeUsec < totalGapUsec)
    {
      pScan->elapsedUsec += remTimeUsec;
      return 0;
    }
  }

  /* Reduce time such that advertising exchange can complete before end of the receive window. */
  remTimeUsec -= totalGapUsec;

  /* Limit scanning to the maximum scan period. */
  remTimeUsec = WSF_MIN(remTimeUsec, (pBbRtCfg->maxScanPeriodMs * 1000));

  return remTimeUsec;
}

/*************************************************************************************************/
/*!
 *  \brief      Continue scan operation.
 *
 *  \param      pBod    Pointer to the BOD to execute.
 *  \param      pScan   Pointer to the scan event operation data.
 *
 *  \return     TRUE if the BOD is completed, FALSE if operation continues.
 *
 *  This function is called to continue scan operation.
 */
/*************************************************************************************************/
static bool_t bbContScanOp(BbOpDesc_t *pBod, BbBleMstAdvEvent_t *pScan)
{
  if (BbGetBodTerminateFlag())
  {
    /* Client terminated. */
    return TRUE;
  }

  uint32_t curTime = PalBbGetCurrentTime();
  pScan->elapsedUsec += BbGetTargetTimeDelta(curTime, bbBleCb.lastScanStartUsec);
  bbBleCb.lastScanStartUsec = curTime;

  uint32_t scanDurUsec;

  if ((scanDurUsec = bbBleCalcScanDurationUsec(pBod, pScan, curTime, BbGetSchSetupDelayUs())) == 0)
  {
    return TRUE;
  }

  bbBleCb.bbParam.dueUsec = BbAdjustTime(bbBleCb.lastScanStartUsec + BbGetSchSetupDelayUs());
  bbBleCb.bbParam.rxTimeoutUsec = scanDurUsec;
  PalBbBleSetDataParams(&bbBleCb.bbParam);

  bbBleCb.evtState = BB_EVT_STATE_RX_ADV_IND;

  if (pScan->pTxReqBuf)
  {
    bbBleSetTifs();   /* active scan or initiating */
  }
  else
  {
    bbBleClrIfs();    /* passive scan */
  }
  PalBbBleRxData(pScan->pRxAdvBuf, BB_ADVB_MAX_LEN);

  return FALSE;
}

/*************************************************************************************************/
/*!
 *  \brief      Tx completion for scanning master operation.
 *
 *  \param      status      Completion status.
 *
 *  Setup for next action in the operation or complete the operation.
 */
/*************************************************************************************************/
static void bbMstScanTxCompCback(uint8_t status)
{
  BB_ISR_START();

  WSF_ASSERT(BbGetCurrentBod());

  BbOpDesc_t * const pCur = BbGetCurrentBod();
  BbBleMstAdvEvent_t * const pScan = &pCur->prot.pBle->op.mstAdv;

  bool_t bodComplete = FALSE;
  bool_t bodCont = FALSE;

#if (BB_SNIFFER_ENABLED == TRUE)
  /* Save evtState to be used later in packet forwarding. */
  uint8_t evtState = bbBleCb.evtState;
  BbBleSnifferPkt_t * pPkt = NULL;
  if (bbSnifferCtx.enabled)
  {
    pPkt = bbSnifferCtx.snifferGetPktFn();
  }
#endif

  switch (bbBleCb.evtState++)
  {
    case BB_EVT_STATE_TX_SCAN_OR_CONN_INIT:
    {
      switch (status)
      {
        case BB_STATUS_SUCCESS:
        {
          WSF_ASSERT(pScan->txReqCback);
          WSF_ASSERT(pScan->pTxReqBuf);

          if (pScan->txReqCback(pCur, pScan->pTxReqBuf))
          {
            WSF_ASSERT(pScan->pRxRspBuf);

            BB_ISR_MARK(bbScanStats.rxSetupUsec);

            bbBleClrIfs();      /* last operation in event */
            PalBbBleRxTifsData(pScan->pRxRspBuf, BB_ADVB_MAX_LEN);

            /* Rx may fail; no more important statements in the !bodComplete code path */
          }
          else
          {
            bodCont = TRUE;
          }

          break;
        }

        case BB_STATUS_FAILED:
        default:
          bodComplete = TRUE;
          break;
      }

      /* Update statistics. */
      switch (status)
      {
        case BB_STATUS_SUCCESS:
          BB_INC_STAT(bbScanStats.txReq);
          break;
        default:
          BB_INC_STAT(bbScanStats.errScan);
          break;
      }

      break;
    }

    default:          /* unexpected state */
      WSF_ASSERT(FALSE);
      break;
  }

  if (bodCont)
  {
    /* Cancel TIFS timer if active. */
    /* coverity[dead_error_condition] */
    switch (status)
    {
      case BB_STATUS_SUCCESS:
        PalBbBleCancelTifs();
        break;
      default:
        /* coverity[dead_error_begin] */
        break;
    }

    bodComplete = bbContScanOp(pCur, pScan);
  }

  if (bodComplete)
  {
    BbTerminateBod();
  }

#if (BB_SNIFFER_ENABLED == TRUE)
  if (pPkt)
  {
    pPkt->pktType.meta.type = BB_SNIFF_PKT_TYPE_TX;
    pPkt->pktType.meta.status = status;
    pPkt->pktType.meta.state = evtState;

    bbBleSnifferMstScanPktHandler(pCur, pPkt);
  }
#endif

  BB_ISR_MARK(bbScanStats.txIsrUsec);
}

/*************************************************************************************************/
/*!
 *  \brief      Rx completion for scanning master operation.
 *
 *  \param      status          Reception status.
 *  \param      rssi            RSSI value.
 *  \param      crc             CRC value.
 *  \param      timestamp       Start of packet timestamp in microseconds.
 *  \param      rxPhyOptions    Rx PHY options.
 *
 *  Setup for next action in the operation or complete the operation.
 */
/*************************************************************************************************/
static void bbMstScanRxCompCback(uint8_t status, int8_t rssi, uint32_t crc, uint32_t timestamp, uint8_t rxPhyOptions)
{
  BB_ISR_START();

  WSF_ASSERT(BbGetCurrentBod());

  BbOpDesc_t * const pCur = BbGetCurrentBod();
  BbBleData_t * const pBle = pCur->prot.pBle;
  BbBleMstAdvEvent_t * const pScan = &pBle->op.mstAdv;

  bool_t bodComplete = FALSE;
  bool_t bodCont = FALSE;

#if (BB_SNIFFER_ENABLED == TRUE)
  /* Save evtState to be used later in packet forwarding. */
  uint8_t evtState = bbBleCb.evtState;
  BbBleSnifferPkt_t * pPkt = NULL;
  if (bbSnifferCtx.enabled)
  {
    pPkt = bbSnifferCtx.snifferGetPktFn();
  }
#endif

  switch (bbBleCb.evtState++)
  {
    case BB_EVT_STATE_RX_ADV_IND:
    {
      switch (status)
      {
        case BB_STATUS_SUCCESS:
        {
          WSF_ASSERT(pScan->rxAdvCback);
          WSF_ASSERT(pScan->pRxAdvBuf);

          pScan->advRssi = rssi;
          pScan->advCrc = crc;
          pScan->advStartTsUsec = timestamp;
          pScan->advRxPhyOptions = rxPhyOptions;

          bool_t pduAllow = BbBlePduFiltCheck(pScan->pRxAdvBuf, &pBle->pduFilt, FALSE, &pScan->filtResults);

          if (pduAllow && pScan->rxAdvCback(pCur, pScan->pRxAdvBuf))
          {
            WSF_ASSERT(pScan->pTxReqBuf);

            BB_ISR_MARK(bbScanStats.txSetupUsec);

            PalBbBleTxBufDesc_t desc = {.pBuf = pScan->pTxReqBuf, .len = pScan->txReqLen};

            bbBleSetTifs();
            PalBbBleTxTifsData(&desc, 1);

            /* Tx may fail; no more important statements in the !bodComplete code path */
          }
          else
          {
            bodCont = TRUE;
          }

#if (BB_SNIFFER_ENABLED == TRUE)
          /* Pack the rx buffer here before it is overwritten. */
          if (pPkt)
          {
            memcpy(pPkt->pktType.advPkt.hdr, pScan->pRxAdvBuf, LL_ADV_HDR_LEN);
          }
#endif

          if (pduAllow && pScan->rxAdvPostCback)
          {
            pScan->rxAdvPostCback(pCur, pScan->pRxAdvBuf);
          }

          break;
        }

        case BB_STATUS_RX_TIMEOUT:
        case BB_STATUS_CRC_FAILED:
          bodCont = TRUE;
          break;

        case BB_STATUS_FAILED:
        default:
          bodComplete = TRUE;
          break;
      }

      /* Update statistics. */
      switch (status)
      {
        case BB_STATUS_SUCCESS:
          BB_INC_STAT(bbScanStats.rxAdv);
          break;
        case BB_STATUS_RX_TIMEOUT:
          BB_INC_STAT(bbScanStats.rxAdvTimeout);
          break;
        case BB_STATUS_CRC_FAILED:
          BB_INC_STAT(bbScanStats.rxAdvCrc);
          break;
        case BB_STATUS_FAILED:
        default:
          BB_INC_STAT(bbScanStats.errScan);
          break;
      }

      break;
    }

    case BB_EVT_STATE_RX_SCAN_OR_CONN_RSP:
    {
      WSF_ASSERT(pScan->rxRspCback);
      WSF_ASSERT(pScan->pRxRspBuf);

      switch (status)
      {
        case BB_STATUS_SUCCESS:
#if (BB_SNIFFER_ENABLED == TRUE)
          /* If the rx was successful, pack the rx sniffer header here before it is overwritten. */
          if (pPkt)
          {
            memcpy(pPkt->pktType.advPkt.hdr, pScan->pRxRspBuf, LL_ADV_HDR_LEN);
          }
#endif

          if (BbBlePduFiltCheck(pScan->pRxRspBuf, &pBle->pduFilt, FALSE, &pScan->filtResults))
          {
            pScan->rxRspCback(pCur, pScan->pRxRspBuf);
          }
          bodCont = TRUE;
          break;

        case BB_STATUS_RX_TIMEOUT:
        case BB_STATUS_CRC_FAILED:
          pScan->rxRspCback(pCur, NULL);
          bodCont = TRUE;
          break;

        case BB_STATUS_FAILED:
        default:
          bodComplete = TRUE;
          break;
      }

      /* Update statistics. */
      switch (status)
      {
        case BB_STATUS_SUCCESS:
          BB_INC_STAT(bbScanStats.rxRsp);
          break;
        case BB_STATUS_RX_TIMEOUT:
          BB_INC_STAT(bbScanStats.rxRspTimeout);
          break;
        case BB_STATUS_CRC_FAILED:
          BB_INC_STAT(bbScanStats.rxRspCrc);
          break;
        case BB_STATUS_FAILED:
        default:
          BB_INC_STAT(bbScanStats.errScan);
          break;
      }

      break;
    }

    default:        /* invalid state */
      WSF_ASSERT(FALSE);
      break;
  }

  if (bodCont)
  {
    /* Cancel TIFS timer if active. */
    switch (status)
    {
      case BB_STATUS_SUCCESS:
      case BB_STATUS_CRC_FAILED:
        PalBbBleCancelTifs();
        break;
      default:
        break;
    }

    bodComplete = bbContScanOp(pCur, pScan);
  }

  if (bodComplete)
  {
    BbTerminateBod();
  }

#if (BB_SNIFFER_ENABLED == TRUE)
  if (pPkt)
  {
    pPkt->pktType.meta.type = BB_SNIFF_PKT_TYPE_RX;
    pPkt->pktType.meta.rssi = rssi;
    pPkt->pktType.meta.timeStamp = timestamp;
    pPkt->pktType.meta.status = status;
    pPkt->pktType.meta.state = evtState;

    bbBleSnifferMstScanPktHandler(pCur, pPkt);
  }
#endif

  BB_ISR_MARK(bbScanStats.rxIsrUsec);
}

/*************************************************************************************************/
/*!
 *  \brief      Execute scanning master BOD.
 *
 *  \param      pBod    Pointer to the BOD to execute.
 *  \param      pBle    BLE operation parameters.
 */
/*************************************************************************************************/
static void bbMstExecuteScanOp(BbOpDesc_t *pBod, BbBleData_t *pBle)
{
  BbBleMstAdvEvent_t * const pScan = &pBod->prot.pBle->op.mstAdv;

  if (pScan->preExecCback)
  {
    pScan->preExecCback(pBod);
  }

  uint32_t scanDurUsec;

  if ((scanDurUsec = bbBleCalcScanDurationUsec(pBod, pScan, pBod->dueUsec, 0)) == 0)
  {
    BbSetBodTerminateFlag();
    return;
  }

  bbBleCb.lastScanStartUsec = pBod->dueUsec;

  PalBbBleSetChannelParam(&pBle->chan);

  bbBleCb.bbParam.txCback       = bbMstScanTxCompCback;
  bbBleCb.bbParam.rxCback       = bbMstScanRxCompCback;
  bbBleCb.bbParam.rxTimeoutUsec = scanDurUsec;
  bbBleCb.bbParam.dueUsec       = BbAdjustTime(pBod->dueUsec);
  pBod->dueUsec = bbBleCb.bbParam.dueUsec;
  PalBbBleSetDataParams(&bbBleCb.bbParam);

  bbBleCb.evtState = 0;

  if (pScan->pTxReqBuf)
  {
    bbBleSetTifs();   /* active scan or initiating */
  }
  else
  {
    bbBleClrIfs();    /* passive scan */
  }
  PalBbBleRxData(pScan->pRxAdvBuf, BB_ADVB_MAX_LEN);

  /* Rx may fail; no more important statements in the !bodComplete code path */
}

/*************************************************************************************************/
/*!
 *  \brief      Cancel scan BOD.
 *
 *  \param      pBod    Pointer to the BOD to execute.
 *  \param      pBle    BLE operation parameters.
 */
/*************************************************************************************************/
static void bbMstCancelScanOp(BbOpDesc_t *pBod, BbBleData_t *pBle)
{
  PalBbBleCancelData();
}

/*************************************************************************************************/
/*!
 *  \brief      Initialize for scanning master operations.
 *
 *  Update the operation table with scanning master operations routines.
 */
/*************************************************************************************************/
void BbBleScanMasterInit(void)
{
  bbBleRegisterOp(BB_BLE_OP_MST_ADV_EVENT, bbMstExecuteScanOp, bbMstCancelScanOp);

  memset(&bbScanStats, 0, sizeof(bbScanStats));
}

/*************************************************************************************************/
/*!
 *  \brief      Get advertising packet statistics.
 */
/*************************************************************************************************/
void BbBleGetScanStats(BbBleScanPktStats_t *pStats)
{
  *pStats = bbScanStats;
}