Newer
Older
mbed-os / connectivity / FEATURE_BLE / cordio / TARGET_CORDIO_LL / stack / controller / sources / common / chci / chci_tr.c
@Paul Szczeanek Paul Szczeanek on 7 Aug 2020 20 KB remove generic, TPPs, nested namespaces
/*************************************************************************************************/
/*!
 *  \file
 *
 *  \brief  Controller HCI transport module 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 "chci_tr.h"
#include "chci_api.h"
#include "hci_defs.h"
#include "wsf_assert.h"
#include "wsf_msg.h"
#include "wsf_math.h"
#include "wsf_os.h"
#include "util/bstream.h"
#include <string.h>

#if (CHCI_TR_UART == 1)
#include "pal_uart.h"
#include "pal_sys.h"
#endif

#if (CHCI_TR_CUSTOM == 1)
/* Custom transport */
extern void CustomChciTrInit(uint16_t maxAclLen, uint16_t maxIsoSduLen);
extern void CustomChciTrWrite(uint8_t prot, uint8_t type, uint16_t len, uint8_t *pData);
#endif

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

/*! \brief      Maximum read buffer length allowed by UART DMA. */
#define LHCI_MAX_RD_BUF_LEN     255

/*! \brief      Maximum write buffer length allowed by UART DMA. */
#define LHCI_MAX_WR_BUF_LEN     255

/*! \brief      Maximum read header length. */
#define LHCI_MAX_RD_HDR_LEN     (1 + 4)     /* type + max header length */

/*! \brief      Maximum write buffer length allowed by UART DMA. */
#define LHCI_MAX_DATA_LEN       4096

/**************************************************************************************************
  Data Types
**************************************************************************************************/

/*! \brief      Transport control block. */
typedef struct
{
  wsfHandlerId_t              handlerId;            /*!< Handler ID. */
  uint8_t                     *pDataPending;        /*!< Data packet in progress. */
  uint8_t                     typePending;          /*!< Data type in progress. */
  uint8_t                     protPending;          /*!< Protocol in progress. */
  uint8_t                     nextAvailMask;        /*!< Next available mask. */

  uint16_t                    maxAclLen;            /*!< Maximum ACL data length. */
  uint16_t                    maxIsoSduLen;         /*!< Maximum ISO data length. */

  uint32_t                    cmdCount;             /*!< Counter of commands */
  uint32_t                    evtCount;             /*!< Counter of events */

  ChciTrSendHwErrorCback_t    sendHwErrorCback;     /*!< Send HW error callback. */

  struct
  {
    ChciTrRecvCback_t         recvCback;            /*!< Message received callback. */
    ChciTrSendCompleteCback_t sendCompleteCback;    /*!< Message send complete callback. */
    ChciTrServiceCback_t      serviceCback;         /*!< Service callback. */
  } protCbacks[CHCI_TR_PROT_NUM];                   /*!< Callback array indexed by protocol ID. */

  /* Read buffer state. */
  uint8_t                     rxPktState;           /*!< Receive state. */
  uint8_t                     rdHdr[LHCI_MAX_RD_HDR_LEN];   /*!< Read header buffer. */
  uint16_t                    rdBufOffs;            /*!< Write data buffer offset. */
  uint16_t                    rdBufLen;             /*!< Write data buffer length. */
  uint8_t                     *pRdBuf;              /*!< Read data buffer. */

  /* Write buffer state. */
  uint16_t                    wrBufOffs;            /*!< Write data buffer offset. */
  uint16_t                    wrBufLen;             /*!< Write data buffer length. */
  uint8_t                     *pWrBuf;              /*!< Write data buffer. */
  bool_t                      wrBufComp;            /*!< Write buffer completed. */
} chciTrCtrlBlk_t;

/*! \brief      Send handler. */
typedef void (*chciTrSendHandler_t)(uint8_t *pBuf);

/*! \brief      Transport events. */
enum
{
  CHCI_TR_EVT_SEND_CMPL = (1 << 0)                  /*!< Send complete. */
};

/*! \brief      Receive state. */
enum
{
  CHCI_RX_STATE_IDLE,               /*!< Idle state. */
  CHCI_RX_STATE_TYPE,               /*!< Type parse state. */
  CHCI_RX_STATE_HEADER,             /*!< Header parse state. */
  CHCI_RX_STATE_PAYLOAD,            /*!< Payload parse state. */
  CHCI_RX_STATE_COMPLETE            /*!< Complete state. */
};

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

/*! \brief      Current event data in progress. */
chciTrCtrlBlk_t chciTrCb;

/*************************************************************************************************/
/*!
 *  \brief  Increment the command and event counters.
 *
 *  \param  type          Type of message.
 */
/*************************************************************************************************/
static void chciTrIncrementCounters(uint8_t type)
{
  if (type == CHCI_TR_TYPE_CMD)
  {
    chciTrCb.cmdCount++;
  }
  else if (type == CHCI_TR_TYPE_EVT)
  {
    chciTrCb.evtCount++;
  }
}

#if (CHCI_TR_UART == 1)

/*************************************************************************************************/
/*!
 *  \brief  Signal a hardware error.
 *
 *  \param  code    Error code.
 */
/*************************************************************************************************/
static void chciTrHwError(uint8_t code)
{
  if (chciTrCb.sendHwErrorCback != NULL)
  {
    chciTrCb.sendHwErrorCback(code);
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Setup a read data buffer.
 *
 *  \param  len       Number of bytes to write.
 *  \param  pData     Byte array to write.
 */
/*************************************************************************************************/
static void chciTrRead(uint16_t len, uint8_t *pData)
{
  WSF_ASSERT(len <= LHCI_MAX_RD_BUF_LEN);
#if (CHCI_TR_UART == 1)
  PalUartReadData(PAL_UART_ID_CHCI, pData, len);
#endif
}

/*************************************************************************************************/
/*!
 *  \brief  Receive packet state machine.
 */
/*************************************************************************************************/
static void chciRxPacketSM(void)
{
  /* --- Type State --- */
  if (chciTrCb.rxPktState == CHCI_RX_STATE_TYPE)
  {
    chciTrCb.rxPktState = CHCI_RX_STATE_HEADER;
    /* Determine header length based on packet type. */
    switch (chciTrCb.rdHdr[0])
    {
      case HCI_CMD_TYPE:
        chciTrRead(HCI_CMD_HDR_LEN, &chciTrCb.rdHdr[1]);
        break;
      case HCI_ACL_TYPE:
        chciTrRead(HCI_ACL_HDR_LEN, &chciTrCb.rdHdr[1]);
        break;
      case HCI_ISO_TYPE:
        chciTrRead(HCI_ISO_HDR_LEN, &chciTrCb.rdHdr[1]);
        break;
      case CHCI_15P4_CMD_TYPE:
      case CHCI_15P4_DATA_TYPE:
        chciTrRead(CHCI_15P4_HDR_LEN, &chciTrCb.rdHdr[1]);
        break;
      default:
        /* Invalid byte received. */
        chciTrHwError(CHCI_TR_CODE_INVALID_DATA);
        chciTrCb.rxPktState = CHCI_RX_STATE_IDLE;
    }
  }

  /* --- Header State --- */
  else if (chciTrCb.rxPktState == CHCI_RX_STATE_HEADER)
  {
    uint8_t hdrLen = 0;
    uint16_t dataLen = 0;
    uint16_t allocLen = 0;

    /* Extract data length from header. */
    switch (chciTrCb.rdHdr[0])
    {
      case HCI_CMD_TYPE:
        hdrLen = HCI_CMD_HDR_LEN;
        dataLen = (uint16_t)chciTrCb.rdHdr[3];
        allocLen = dataLen;
        break;
      case HCI_ACL_TYPE:
        hdrLen = HCI_ACL_HDR_LEN;
        BYTES_TO_UINT16(dataLen, &chciTrCb.rdHdr[3]);
        if (dataLen > chciTrCb.maxAclLen)
        {
          chciTrHwError(CHCI_TR_CODE_INVALID_DATA_LEN);
          chciTrCb.rxPktState = CHCI_RX_STATE_IDLE;
          return;
        }
        /* Use data buffers located in the large pool. */
        allocLen = chciTrCb.maxAclLen + CHCI_BUF_TAILROOM;
        break;
      case HCI_ISO_TYPE:
        hdrLen = HCI_ISO_HDR_LEN;
        BYTES_TO_UINT16(dataLen, &chciTrCb.rdHdr[3]);
        if (dataLen > (chciTrCb.maxIsoSduLen + HCI_ISO_DL_MAX_LEN))
        {
          chciTrHwError(CHCI_TR_CODE_INVALID_DATA_LEN);
          chciTrCb.rxPktState = CHCI_RX_STATE_IDLE;
          return;
        }
        /* Use data buffers located in the large pool. */
        allocLen = HCI_ISO_DL_MAX_LEN + chciTrCb.maxIsoSduLen + CHCI_BUF_TAILROOM;
        break;
      case CHCI_15P4_CMD_TYPE:
      case CHCI_15P4_DATA_TYPE:
        hdrLen = CHCI_15P4_HDR_LEN;
        BYTES_TO_UINT16(dataLen, &chciTrCb.rdHdr[2]);
        allocLen = dataLen;
        break;
      default:
        /* already validated in CHCI_RX_STATE_TYPE */
        break;
    }

    if (dataLen > LHCI_MAX_DATA_LEN)
    {
      /* Invalid byte received. */
      chciTrHwError(CHCI_TR_CODE_INVALID_DATA);
      chciTrCb.rxPktState = CHCI_RX_STATE_IDLE;
    }
    else if ((chciTrCb.pRdBuf = (uint8_t *)WsfMsgAlloc(hdrLen + allocLen)) != NULL)
    {
      if (dataLen > 0)
      {
        /* Bound read size to UART DMA maximum length. */
        uint16_t blkLen = WSF_MIN(dataLen, LHCI_MAX_RD_BUF_LEN);

        /* Read additional payload data. */
        chciTrRead(blkLen, chciTrCb.pRdBuf + hdrLen);
        chciTrCb.rdBufLen = hdrLen + dataLen;
        chciTrCb.rdBufOffs = hdrLen + blkLen;
        chciTrCb.rxPktState = CHCI_RX_STATE_PAYLOAD;
      }
      else
      {
        /* No more data. */
        chciTrCb.rxPktState = CHCI_RX_STATE_COMPLETE;
      }
      /* Remove packet type encapsulation. */
      memcpy(chciTrCb.pRdBuf, &chciTrCb.rdHdr[1], hdrLen);
    }
    else
    {
      chciTrHwError(CHCI_TR_CODE_OUT_OF_MEMORY);
      chciTrCb.rxPktState = CHCI_RX_STATE_IDLE;
    }
  }

  /* --- Data State --- */
  else if (chciTrCb.rxPktState == CHCI_RX_STATE_PAYLOAD)
  {
    uint16_t blkLen = WSF_MIN((chciTrCb.rdBufLen - chciTrCb.rdBufOffs), LHCI_MAX_RD_BUF_LEN);
    if (blkLen > 0)
    {
      /* Bound read size to UART DMA maximum length. */
      blkLen = WSF_MIN(blkLen, LHCI_MAX_RD_BUF_LEN);

      /* Read next block. */
      chciTrRead(blkLen, chciTrCb.pRdBuf + chciTrCb.rdBufOffs);
      chciTrCb.rdBufOffs += blkLen;
    }
    else
    {
      chciTrCb.rxPktState = CHCI_RX_STATE_COMPLETE;
    }
  }

  /* --- Complete State --- */
  /* N.B. There is no else-if construct by design. */
  if (chciTrCb.rxPktState == CHCI_RX_STATE_COMPLETE)
  {
    WSF_ASSERT(chciTrCb.pRdBuf);

    switch (chciTrCb.rdHdr[0])
    {
      case HCI_ISO_TYPE:
        chciTrRecv(CHCI_TR_PROT_BLE, CHCI_TR_TYPE_ISO, chciTrCb.pRdBuf);
        break;
      case HCI_ACL_TYPE:
        chciTrRecv(CHCI_TR_PROT_BLE, CHCI_TR_TYPE_ACL, chciTrCb.pRdBuf);
        break;
      case HCI_CMD_TYPE:
        chciTrRecv(CHCI_TR_PROT_BLE, CHCI_TR_TYPE_CMD, chciTrCb.pRdBuf);
        break;
      case CHCI_15P4_CMD_TYPE:
        chciTrRecv(CHCI_TR_PROT_15P4, CHCI_TR_TYPE_CMD, chciTrCb.pRdBuf);
        break;
      case CHCI_15P4_DATA_TYPE:
        chciTrRecv(CHCI_TR_PROT_15P4, CHCI_TR_TYPE_ACL, chciTrCb.pRdBuf);
        break;
      default:
        break;
    }

    chciTrCb.pRdBuf = NULL;

    chciTrCb.rxPktState = CHCI_RX_STATE_IDLE;
  }

  /* --- Idle State --- */
  /* N.B. There is no else-if construct by design. */
  if (chciTrCb.rxPktState == CHCI_RX_STATE_IDLE)
  {
    /* Read packet type. */
    chciTrRead(1, &chciTrCb.rdHdr[0]);
    chciTrCb.rxPktState = CHCI_RX_STATE_TYPE;
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Tx complete callback.
 */
/*************************************************************************************************/
static void chciTxComplete(void)
{
  uint16_t blkLen = WSF_MIN((chciTrCb.wrBufLen - chciTrCb.wrBufOffs), LHCI_MAX_WR_BUF_LEN);

  if (blkLen)
  {
  #if (CHCI_TR_UART == 1)
    PalUartWriteData(PAL_UART_ID_CHCI, chciTrCb.pWrBuf + chciTrCb.wrBufOffs, blkLen);
  #endif
    chciTrCb.wrBufOffs += blkLen;
  }
  else
  {
    /* Completion handled in task context, i.e. ChciTrService(). */
    chciTrCb.wrBufComp = TRUE;
  }
}

#endif /* (CHCI_TR_UART == 1) */

/*************************************************************************************************/
/*!
 *  \brief  Write data the driver.
 *
 *  \param  prot      Protocol.
 *  \param  type      Packet type.
 *  \param  len       Number of bytes to write.
 *  \param  pData     Byte array to write.
 *
 *  \note   The type parameter allows the driver layer to prepend the data with a header on the
 *          same write transaction.
 */
/*************************************************************************************************/
static void chciTrWrite(uint8_t prot, uint8_t type, uint16_t len, uint8_t *pData)
{
#if (CHCI_TR_UART == 1)
  /* Initialize write buffer state. */
  chciTrCb.wrBufLen  = len + 1;      /* use wsfMsg_t headroom */
  if (prot == CHCI_TR_PROT_15P4)
  {
    *(pData - 1) = (type == CHCI_TR_TYPE_EVT) ? CHCI_15P4_CMD_TYPE : CHCI_15P4_DATA_TYPE;
  }
  else
  {
    if (type == CHCI_TR_TYPE_EVT)
    {
      *(pData - 1) = HCI_EVT_TYPE;
    }
    else if (type == CHCI_TR_TYPE_ACL)
    {
      *(pData - 1) = HCI_ACL_TYPE;
    }
    else
    {
      /* CHCI_TR_TYPE_ISO */
      *(pData - 1) = HCI_ISO_TYPE;
    }
  }
  chciTrCb.pWrBuf    = pData - 1;
  chciTrCb.wrBufOffs = WSF_MIN(chciTrCb.wrBufLen, LHCI_MAX_WR_BUF_LEN);

  /* Initiate Tx operation. */
  PalUartWriteData(PAL_UART_ID_CHCI, chciTrCb.pWrBuf, chciTrCb.wrBufOffs);
  PalSysSetBusy();
#endif

#if (CHCI_TR_CUSTOM == 1)
  CustomChciTrWrite(prot, type, len, pData);
  chciTrSendComplete();
#endif
}

/*************************************************************************************************/
/*!
 *  \brief  Signal the completion of a message write.
 *
 *  This routine is used for asynchronous write operations. When the driver has completed the
 *  use of the write buffer, this routine is called to free the buffer and release flow control.
 */
/*************************************************************************************************/
void chciTrSendComplete(void)
{
  uint8_t *pBuf = chciTrCb.pDataPending;
  uint8_t type  = chciTrCb.typePending;
  uint8_t prot  = chciTrCb.protPending;

  WSF_ASSERT(chciTrCb.protCbacks[prot].sendCompleteCback != NULL);

  /* Protocol will free buffer. */
  chciTrCb.pDataPending = NULL;
  chciTrCb.protCbacks[prot].sendCompleteCback(type, pBuf);
  WsfSetEvent(chciTrCb.handlerId, CHCI_TR_EVT_SEND_CMPL);
}

/*************************************************************************************************/
/*!
 *  \brief  Get the HCI command and event counts.
 *
 *  \param  pCmdCount       Pointer to uint32_t to hold command count.
 *  \param  pEvtCount       Pointer to uint32_t to hold event count.
 */
/*************************************************************************************************/
void ChciTrGetCmdEvtCounts(uint32_t *pCmdCount, uint32_t *pEvtCount)
{
  *pCmdCount = chciTrCb.cmdCount;
  *pEvtCount = chciTrCb.evtCount;
}

/*************************************************************************************************/
/*!
 *  \brief  Reset the HCI command and event counts to zero.
 */
/*************************************************************************************************/
void ChciTrResetCmdEvtCounts(void)
{
  chciTrCb.cmdCount = 0;
  chciTrCb.evtCount = 0;
}

/*************************************************************************************************/
/*!
 *  \brief  Initialize the transport handler.
 *
 *  \param  handlerId       Handler ID.
 *  \param  maxAclLen       Maximum ACL data length.
 *  \param  maxIsoSduLen    Maximum ISO data length.
 */
/*************************************************************************************************/
void ChciTrHandlerInit(wsfHandlerId_t handlerId, uint16_t maxAclLen, uint16_t maxIsoSduLen)
{
  memset(&chciTrCb, 0, sizeof(chciTrCb));
  chciTrCb.handlerId = handlerId;
  chciTrCb.maxAclLen = maxAclLen;
  chciTrCb.maxIsoSduLen = maxIsoSduLen;

#if (CHCI_TR_UART == 1)
  PalUartConfig_t cfg;
  cfg.baud   = UART_BAUD;
  cfg.hwFlow = UART_HWFC;
  cfg.rdCback = chciRxPacketSM;
  cfg.wrCback = chciTxComplete;
  PalUartInit(PAL_UART_ID_CHCI, &cfg);

  /* Start receiver. */
  chciRxPacketSM();
#elif (CHCI_TR_CUSTOM == 1)
  CustomChciTrInit(maxAclLen, maxIsoSduLen);
#else
  (void)chciRxPacketSM;
  (void)chciTxComplete;
#endif
}

/*************************************************************************************************/
/*!
 *  \brief      Controller HCI transport message dispatch handler.
 *
 *  \param      event       WSF event.
 *  \param      pMsg        WSF message.
 */
/*************************************************************************************************/
void ChciTrHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
  if (chciTrCb.pDataPending == NULL)
  {
    uint8_t protCount = 0;
    uint8_t prot      = chciTrCb.protPending;   /* last protocol serviced */

    do
    {
      uint8_t  type = 0;
      uint8_t  *pData = NULL;
      uint16_t len = 0;

      prot = (prot + 1 >= CHCI_TR_PROT_NUM) ? 0 : (prot + 1);

      if ((chciTrCb.protCbacks[prot].serviceCback != NULL) &&
          chciTrCb.protCbacks[prot].serviceCback(&type, &len, &pData))
      {
        chciTrCb.pDataPending = pData;
        chciTrCb.typePending  = type;
        chciTrCb.protPending  = prot;

        chciTrIncrementCounters(type);
        chciTrWrite(prot, type, len, pData);
        break;
      }
    } while (++protCount < CHCI_TR_PROT_NUM);
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Set callbacks for a protocol.
 *
 *  \param  prot                Protocol.
 *  \param  recvCback           Message received callback.
 *  \param  sendCompleteCback   Message send complete callback.
 *  \param  serviceCback        Service callback.
 */
/*************************************************************************************************/
void ChciTrSetCbacks(uint8_t prot, ChciTrRecvCback_t recvCback, ChciTrSendCompleteCback_t sendCompleteCback,
    ChciTrServiceCback_t serviceCback)
{
  if (prot < CHCI_TR_PROT_NUM)
  {
    chciTrCb.protCbacks[prot].recvCback         = recvCback;
    chciTrCb.protCbacks[prot].sendCompleteCback = sendCompleteCback;
    chciTrCb.protCbacks[prot].serviceCback      = serviceCback;
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Set send hardware error callback.
 *
 *  \param  sendHwErrorCback    Send hardware error callback.
 */
/*************************************************************************************************/
void ChciTrSetSendHwErrorCback(ChciTrSendHwErrorCback_t sendHwErrorCback)
{
  chciTrCb.sendHwErrorCback = sendHwErrorCback;
}

/*************************************************************************************************/
/*!
 *  \brief  Flag protocol for needing service.
 *
 *  \param  prot                Protocol.
 */
/*************************************************************************************************/
void ChciTrNeedsService(uint8_t prot)
{
  if (chciTrCb.pDataPending == NULL)
  {
    ChciTrHandler(CHCI_TR_EVT_SEND_CMPL, NULL);
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Signal the completion of a message receive.
 *
 *  \param  prot    Protocol.
 *  \param  type    Message type.
 *  \param  pBuf    Message.
 */
/*************************************************************************************************/
void chciTrRecv(uint8_t prot, uint8_t type, uint8_t *pBuf)
{
  chciTrIncrementCounters(type);

  if (prot < CHCI_TR_PROT_NUM)
  {
    if (chciTrCb.protCbacks[prot].recvCback != NULL)
    {
      /* Protocol will free buffer. */
      chciTrCb.protCbacks[prot].recvCback(type, pBuf);
      return;
    }
  }

  /* Free buffer that cannot be handled. */
  WsfMsgFree(pBuf);
}

/*************************************************************************************************/
/*!
 *  \brief  Service the transport device.
 *
 *  \return TRUE if work pending, FALSE if no work is pending.
 */
/*************************************************************************************************/
bool_t ChciTrService(void)
{
#if (CHCI_TR_UART == 1)
  if (chciTrCb.wrBufComp)
  {
    chciTrCb.wrBufComp = FALSE;
    chciTrSendComplete();
    PalSysSetIdle();
    return TRUE;
  }
#endif

  return FALSE;
}