/*************************************************************************************************/ /*! * \file hci_tr.c * * \brief HCI transport module. * * Copyright (c) 2011-2018 Arm Ltd. All Rights Reserved. * * Copyright (c) 2019 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 #include "wsf_types.h" #include "wsf_msg.h" #include "wsf_assert.h" #include "util/bstream.h" #include "hci_api.h" #include "hci_core.h" #include "hci_tr.h" #include "hci_drv.h" /* PORTING: EXACTLE removed as replaced by zero copy hci driver in mbedos */ uint16_t hci_mbed_os_drv_write(uint8_t type, uint16_t len, uint8_t *pData); /************************************************************************************************** Data Types **************************************************************************************************/ typedef enum { HCI_RX_STATE_IDLE, HCI_RX_STATE_HEADER, HCI_RX_STATE_DATA, HCI_RX_STATE_COMPLETE } hciRxState_t; /*************************************************************************************************/ /*! * \fn hciTrSendAclData * * \brief Send a complete HCI ACL packet to the transport. * * \param pContext Connection context. * \param pData WSF msg buffer containing an ACL packet. * * \return None. */ /*************************************************************************************************/ void hciTrSendAclData(void *pContext, uint8_t *pData) { /* PORTING: sending and fragmenting done by mbed-os */ uint16_t len; /* get 16-bit length */ BYTES_TO_UINT16(len, (pData + 2)) len += HCI_ACL_HDR_LEN; /* transmit ACL header and data */ if (hci_mbed_os_drv_write(HCI_ACL_TYPE, len, pData) == len) { #if CORDIO_ZERO_COPY_HCI /* pData is not freed as the hci_mbed_os_drv_write took ownership of the WSF buffer */ #else /* free buffer */ hciCoreTxAclComplete((hciCoreConn_t *)pContext, pData); #endif // CORDIO_ZERO_COPY_HCI } } /*************************************************************************************************/ /*! * \brief Send a complete HCI command to the transport. * * \param pCmdData WSF msg buffer containing an HCI command. * * \return None. */ /*************************************************************************************************/ void hciTrSendCmd(uint8_t *pCmdData) { /* PORTING: sending done by mbed-os */ uint16_t len; /* get length */ len = pCmdData[2] + HCI_CMD_HDR_LEN; /* transmit ACL header and data */ if (hci_mbed_os_drv_write(HCI_CMD_TYPE, len, pCmdData) == len) { #if CORDIO_ZERO_COPY_HCI /* pData is not freed as the hci_mbed_os_drv_write took ownership of the WSF buffer */ #else /* free buffer */ WsfMsgFree(pCmdData); #endif // CORDIO_ZERO_COPY_HCI } } /*************************************************************************************************/ /*! * \fn hciSerialRxIncoming * * \brief Receive function. Gets called by external code when bytes are received. * * \param pBuf Pointer to buffer of incoming bytes. * \param len Number of bytes in incoming buffer. * * \return None. */ /*************************************************************************************************/ void hciTrSerialRxIncoming(uint8_t *pBuf, uint8_t len) { static uint8_t stateRx = HCI_RX_STATE_IDLE; static uint8_t pktIndRx; static uint16_t iRx; static uint8_t hdrRx[HCI_ACL_HDR_LEN]; static uint8_t *pPktRx; static uint8_t *pDataRx; uint8_t dataByte; /* loop until all bytes of incoming buffer are handled */ while (len--) { /* read single byte from incoming buffer and advance to next byte */ dataByte = *pBuf++; /* --- Idle State --- */ if (stateRx == HCI_RX_STATE_IDLE) { /* save the packet type */ pktIndRx = dataByte; iRx = 0; stateRx = HCI_RX_STATE_HEADER; } /* --- Header State --- */ else if (stateRx == HCI_RX_STATE_HEADER) { uint8_t hdrLen = 0; uint16_t dataLen = 0; /* copy current byte into the temp header buffer */ hdrRx[iRx++] = dataByte; /* determine header length based on packet type */ switch (pktIndRx) { case HCI_CMD_TYPE: hdrLen = HCI_CMD_HDR_LEN; break; case HCI_ACL_TYPE: hdrLen = HCI_ACL_HDR_LEN; break; case HCI_EVT_TYPE: hdrLen = HCI_EVT_HDR_LEN; break; default: /* invalid packet type */ WSF_ASSERT(0); break; } /* see if entire header has been read */ if (iRx == hdrLen) { /* extract data length from header */ switch (pktIndRx) { case HCI_CMD_TYPE: dataLen = hdrRx[2]; break; case HCI_ACL_TYPE: BYTES_TO_UINT16(dataLen, &hdrRx[2]); break; case HCI_EVT_TYPE: dataLen = hdrRx[1]; break; default: break; } /* allocate data buffer to hold entire packet */ if (pktIndRx == HCI_ACL_TYPE) { pPktRx = (uint8_t*)WsfMsgDataAlloc(hdrLen + dataLen, 0); } else { pPktRx = (uint8_t*)WsfMsgAlloc(hdrLen + dataLen); } if (pPktRx != NULL) { pDataRx = pPktRx; /* copy header into data packet (note: memcpy is not so portable) */ { uint8_t i; for (i = 0; i < hdrLen; i++) { *pDataRx++ = hdrRx[i]; } } /* save number of bytes left to read */ iRx = dataLen; if (iRx == 0) { stateRx = HCI_RX_STATE_COMPLETE; } else { stateRx = HCI_RX_STATE_DATA; } } else { WSF_ASSERT(0); /* allocate falied */ } } } /* --- Data State --- */ else if (stateRx == HCI_RX_STATE_DATA) { /* write incoming byte to allocated buffer */ *pDataRx++ = dataByte; /* determine if entire packet has been read */ iRx--; if (iRx == 0) { stateRx = HCI_RX_STATE_COMPLETE; } } /* --- Complete State --- */ /* ( Note Well! There is no else-if construct by design. ) */ if (stateRx == HCI_RX_STATE_COMPLETE) { /* deliver data */ if (pPktRx != NULL) { hciCoreRecv(pktIndRx, pPktRx); } /* reset state machine */ stateRx = HCI_RX_STATE_IDLE; } } }