/*************************************************************************************************/ /*! * \file hci_tr.c * * \brief HCI transport module. * * $Date: 2017-03-10 14:08:37 -0600 (Fri, 10 Mar 2017) $ * $Revision: 11501 $ * * Copyright (c) 2011-2017 ARM Ltd., all rights reserved. * ARM Ltd. confidential and proprietary. * * IMPORTANT. Your use of this file is governed by a Software License Agreement * ("Agreement") that must be accepted in order to download or otherwise receive a * copy of this file. You may not use or copy this file for any purpose other than * as described in the Agreement. If you do not agree to all of the terms of the * Agreement do not use this file and delete all copies in your possession or control; * if you do not have a copy of the Agreement, you must contact ARM Ltd. prior * to any use, copying or further distribution of this software. */ /*************************************************************************************************/ #include "wsf_types.h" #include "wsf_msg.h" #include "wsf_trace.h" #include "wsf_assert.h" #include "bstream.h" #include "hci_api.h" #include "hci_core.h" #include "hci_drv.h" #include "am_mcu_apollo.h" /************************************************************************************************** State variable **************************************************************************************************/ static volatile bool_t g_bHCIReceivingPacket = FALSE; /************************************************************************************************** Macros **************************************************************************************************/ #define HCI_HDR_LEN_MAX HCI_ACL_HDR_LEN /************************************************************************************************** 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) { uint16_t len; /* get 16-bit length */ BYTES_TO_UINT16(len, &pData[2]); len += HCI_ACL_HDR_LEN; /* dump event for protocol analysis */ HCI_PDUMP_TX_ACL(len, pData); /* transmit ACL header and data */ if (hciDrvWrite(HCI_ACL_TYPE, len, pData) == len) { /* free buffer */ hciCoreTxAclComplete(pContext, pData); } } /*************************************************************************************************/ /*! * \fn hciTrSendCmd * * \brief Send a complete HCI command to the transport. * * \param pData WSF msg buffer containing an HCI command. * * \return None. */ /*************************************************************************************************/ void hciTrSendCmd(uint8_t *pData) { uint8_t len; /* get length */ len = pData[2] + HCI_CMD_HDR_LEN; /* dump event for protocol analysis */ HCI_PDUMP_CMD(len, pData); /* transmit ACL header and data */ if (hciDrvWrite(HCI_CMD_TYPE, len, pData) == len) { /* free buffer */ WsfMsgFree(pData); } } /*************************************************************************************************/ /*! * \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 The number of bytes consumed. */ /*************************************************************************************************/ uint16_t hciTrSerialRxIncoming(uint8_t *pBuf, uint16_t len) { static uint8_t stateRx = HCI_RX_STATE_IDLE; static uint8_t pktIndRx; static uint16_t iRx; static uint8_t hdrRx[HCI_HDR_LEN_MAX]; static uint8_t *pPktRx; static uint8_t *pDataRx; uint8_t dataByte; uint16_t consumed_bytes; consumed_bytes = 0; /* 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; g_bHCIReceivingPacket = TRUE; pBuf++; consumed_bytes++; len--; } /* --- Header State --- */ else if (stateRx == HCI_RX_STATE_HEADER) { uint8_t hdrLen = 0; uint16_t dataLen = 0; /* determine header length based on packet type */ if (pktIndRx == HCI_EVT_TYPE) { hdrLen = HCI_EVT_HDR_LEN; } else if (pktIndRx == HCI_ACL_TYPE) { hdrLen = HCI_ACL_HDR_LEN; } else { /* invalid packet type */ WSF_ASSERT(0); return consumed_bytes; } if (iRx != hdrLen) { /* copy current byte into the temp header buffer */ hdrRx[iRx++] = dataByte; pBuf++; consumed_bytes++; len--; } /* see if entire header has been read */ if (iRx == hdrLen) { /* extract data length from header */ if (pktIndRx == HCI_EVT_TYPE) { dataLen = hdrRx[1]; } else if (pktIndRx == HCI_ACL_TYPE) { BYTES_TO_UINT16(dataLen, &hdrRx[2]); } /* 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 */ return consumed_bytes; } } } /* --- 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; } pBuf++; consumed_bytes++; len--; } /* --- Complete State --- */ /* ( Note Well! There is no else-if construct by design. ) */ if (stateRx == HCI_RX_STATE_COMPLETE) { g_bHCIReceivingPacket = FALSE; /* deliver data */ if (pPktRx != NULL) { //am_hal_gpio_out_bit_set(13); hciCoreRecv(pktIndRx, pPktRx); //am_hal_gpio_out_bit_clear(13); } /* reset state machine */ stateRx = HCI_RX_STATE_IDLE; } } return consumed_bytes; } //***************************************************************************** // //! @brief Check to see if the state machine has received part of a packet. //! //! This function checks the HCI packet-receive state machine to see if it is //! in the middle of receiving a packet. This information can be useful in //! determining whether the serial interface should remain enabled. //! //! @return TRUE if there is a packet in progress. // //***************************************************************************** bool_t hciTrReceivingPacket(void) { return g_bHCIReceivingPacket; }