initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -0,0 +1,838 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief HCI core module, platform independent functions.
*
* Copyright (c) 2009-2018 Arm Ltd.
*
* 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.
*
* This module implements core platform independent HCI features for transmit data path,
* fragmentation, reassembly, and connection management.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "util/bda.h"
#include "util/bstream.h"
#include "hci_core.h"
#include "hci_tr.h"
#include "hci_cmd.h"
#include "hci_api.h"
#include "hci_main.h"
#include "l2c_defs.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/* Default ACL buffer flow control watermark levels */
#ifndef HCI_ACL_QUEUE_HI
#define HCI_ACL_QUEUE_HI 5 /* Disable flow when this many buffers queued */
#endif
#ifndef HCI_ACL_QUEUE_LO
#define HCI_ACL_QUEUE_LO 1 /* Enable flow when this many buffers queued */
#endif
/* Default maximum ACL packet size for reassembly */
#ifndef HCI_MAX_RX_ACL_LEN
#define HCI_MAX_RX_ACL_LEN HCI_ACL_DEFAULT_LEN
#endif
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Event mask */
const uint8_t hciEventMask[HCI_EVT_MASK_LEN] =
{
HCI_EVT_MASK_DISCONNECT_CMPL | /* Byte 0 */
HCI_EVT_MASK_ENC_CHANGE, /* Byte 0 */
HCI_EVT_MASK_READ_REMOTE_VER_INFO_CMPL | /* Byte 1 */
HCI_EVT_MASK_HW_ERROR, /* Byte 1 */
0, /* Byte 2 */
HCI_EVT_MASK_DATA_BUF_OVERFLOW, /* Byte 3 */
0, /* Byte 4 */
HCI_EVT_MASK_ENC_KEY_REFRESH_CMPL, /* Byte 5 */
0, /* Byte 6 */
HCI_EVT_MASK_LE_META /* Byte 7 */
};
/* LE event mask */
const uint8_t hciLeEventMask[HCI_LE_EVT_MASK_LEN] =
{
HCI_EVT_MASK_LE_CONN_CMPL_EVT | /* Byte 0 */
HCI_EVT_MASK_LE_ADV_REPORT_EVT | /* Byte 0 */
HCI_EVT_MASK_LE_CONN_UPDATE_CMPL_EVT | /* Byte 0 */
HCI_EVT_MASK_LE_READ_REMOTE_FEAT_CMPL_EVT | /* Byte 0 */
HCI_EVT_MASK_LE_LTK_REQ_EVT | /* Byte 0 */
HCI_EVT_MASK_LE_REMOTE_CONN_PARAM_REQ_EVT | /* Byte 0 */
HCI_EVT_MASK_LE_DATA_LEN_CHANGE_EVT | /* Byte 0 */
HCI_EVT_MASK_LE_READ_LOCAL_P256_PUB_KEY_CMPL, /* Byte 0 */
HCI_EVT_MASK_LE_GENERATE_DHKEY_CMPL | /* Byte 1 */
HCI_EVT_MASK_LE_ENHANCED_CONN_CMPL_EVT | /* Byte 1 */
HCI_EVT_MASK_LE_DIRECT_ADV_REPORT_EVT | /* Byte 1 */
HCI_EVT_MASK_LE_PHY_UPDATE_CMPL_EVT | /* Byte 1 */
HCI_EVT_MASK_LE_EXT_ADV_REPORT_EVT | /* Byte 1 */
HCI_EVT_MASK_LE_PER_ADV_SYNC_EST_EVT | /* Byte 1 */
HCI_EVT_MASK_LE_PER_ADV_REPORT_EVT | /* Byte 1 */
HCI_EVT_MASK_LE_PER_ADV_SYNC_LOST_EVT, /* Byte 1 */
HCI_EVT_MASK_LE_SCAN_TIMEOUT_EVT | /* Byte 2 */
HCI_EVT_MASK_LE_ADV_SET_TERM_EVT | /* Byte 2 */
HCI_EVT_MASK_LE_SCAN_REQ_RCVD_EVT | /* Byte 2 */
HCI_EVT_MASK_LE_CH_SEL_ALGO_EVT | /* Byte 2 */
HCI_EVT_MASK_LE_CONN_IQ_REPORT_EVT | /* Byte 2 */
HCI_EVT_MASK_LE_CTE_REQ_FAILED_EVT | /* Byte 2 */
HCI_EVT_MASK_LE_PER_SYNC_TRSF_RCVT_EVT, /* Byte 2 */
0, /* Byte 3 */
0, /* Byte 4 */
0, /* Byte 5 */
0, /* Byte 6 */
0 /* Byte 7 */
};
/* event mask page 2 */
const uint8_t hciEventMaskPage2[HCI_EVT_MASK_PAGE_2_LEN] =
{
0, /* Byte 0 */
0, /* Byte 1 */
HCI_EVT_MASK_AUTH_PAYLOAD_TIMEOUT, /* Byte 2 */
0, /* Byte 3 */
0, /* Byte 4 */
0, /* Byte 5 */
0, /* Byte 6 */
0 /* Byte 7 */
};
/* LE supported features configuration mask */
uint32_t hciLeSupFeatCfg =
HCI_LE_SUP_FEAT_ENCRYPTION | /* LE Encryption */
HCI_LE_SUP_FEAT_CONN_PARAM_REQ_PROC | /* Connection Parameters Request Procedure */
HCI_LE_SUP_FEAT_EXT_REJECT_IND | /* Extended Reject Indication */
HCI_LE_SUP_FEAT_SLV_INIT_FEAT_EXCH | /* Slave-initiated Features Exchange */
HCI_LE_SUP_FEAT_LE_PING | /* LE Ping */
HCI_LE_SUP_FEAT_DATA_LEN_EXT | /* LE Data Packet Length Extension */
HCI_LE_SUP_FEAT_PRIVACY | /* LL Privacy */
HCI_LE_SUP_FEAT_EXT_SCAN_FILT_POLICY | /* Extended Scanner Filter Policies */
HCI_LE_SUP_FEAT_LE_2M_PHY | /* LE 2M PHY supported */
HCI_LE_SUP_FEAT_STABLE_MOD_IDX_TRANSMITTER | /* Stable Modulation Index - Transmitter supported */
HCI_LE_SUP_FEAT_STABLE_MOD_IDX_RECEIVER | /* Stable Modulation Index - Receiver supported */
HCI_LE_SUP_FEAT_LE_EXT_ADV | /* LE Extended Advertising */
HCI_LE_SUP_FEAT_LE_PER_ADV; /* LE Periodic Advertising */
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/* Control block */
hciCoreCb_t hciCoreCb;
/*************************************************************************************************/
/*!
* \brief Allocate a connection structure.
*
* \param handle Connection handle.
*
* \return None.
*/
/*************************************************************************************************/
static void hciCoreConnAlloc(uint16_t handle)
{
uint8_t i;
hciCoreConn_t *pConn = hciCoreCb.conn;
/* find available connection struct */
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
{
if (pConn->handle == HCI_HANDLE_NONE)
{
/* allocate and initialize */
pConn->handle = handle;
pConn->flowDisabled = FALSE;
pConn->outBufs = 0;
pConn->queuedBufs = 0;
return;
}
}
HCI_TRACE_WARN0("HCI conn struct alloc failure");
}
/*************************************************************************************************/
/*!
* \brief Free a connection structure.
*
* \param handle Connection handle.
*
* \return None.
*/
/*************************************************************************************************/
static void hciCoreConnFree(uint16_t handle)
{
uint8_t i;
hciCoreConn_t *pConn = hciCoreCb.conn;
/* find connection struct */
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
{
if (pConn->handle == handle)
{
/* free any fragmenting ACL packet */
if (pConn->pTxAclPkt != NULL)
{
WsfMsgFree(pConn->pTxAclPkt);
pConn->pTxAclPkt = NULL;
}
pConn->fragmenting = FALSE;
if (pConn->pRxAclPkt != NULL)
{
WsfMsgFree(pConn->pRxAclPkt);
pConn->pRxAclPkt = NULL;
}
/* free structure */
pConn->handle = HCI_HANDLE_NONE;
/* optional: iterate through tx ACL queue and free any buffers with this handle */
/* outstanding buffers are now available; service TX data path */
hciCoreTxReady(pConn->outBufs);
return;
}
}
HCI_TRACE_WARN1("hciCoreConnFree handle not found:%u", handle);
}
/*************************************************************************************************/
/*!
* \brief Get a connection structure by handle
*
* \param handle Connection handle.
*
* \return Pointer to connection structure or NULL if not found.
*/
/*************************************************************************************************/
hciCoreConn_t *hciCoreConnByHandle(uint16_t handle)
{
uint8_t i;
hciCoreConn_t *pConn = hciCoreCb.conn;
/* find available connection struct */
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
{
if (pConn->handle == handle)
{
return pConn;
}
}
return NULL;
}
/*************************************************************************************************/
/*!
* \brief Get the next connection structure with a packet fragment to send.
*
* \return Pointer to connection structure or NULL if not found.
*/
/*************************************************************************************************/
static hciCoreConn_t *hciCoreNextConnFragment(void)
{
uint8_t i;
hciCoreConn_t *pConn = hciCoreCb.conn;
/* find connection struct */
for (i = DM_CONN_MAX; i > 0; i--, pConn++)
{
if (pConn->handle != HCI_HANDLE_NONE && pConn->fragmenting)
{
return pConn;
}
}
return NULL;
}
/*************************************************************************************************/
/*!
* \brief Perform internal processing on HCI connection open.
*
* \param handle Connection handle.
*
* \return None.
*/
/*************************************************************************************************/
void hciCoreConnOpen(uint16_t handle)
{
/* allocate connection structure */
hciCoreConnAlloc(handle);
}
/*************************************************************************************************/
/*!
* \brief Perform internal processing on HCI connection close.
*
* \param handle Connection handle.
*
* \return None.
*/
/*************************************************************************************************/
void hciCoreConnClose(uint16_t handle)
{
/* free connection structure */
hciCoreConnFree(handle);
}
/*************************************************************************************************/
/*!
* \brief Send ACL data to transport.
*
* \param pConn Pointer to connection structure.
* \param pData WSF buffer containing an ACL packet.
*
* \return None.
*/
/*************************************************************************************************/
void hciCoreSendAclData(hciCoreConn_t *pConn, uint8_t *pData)
{
/* increment outstanding buf count for handle */
pConn->outBufs++;
/* send to transport */
hciTrSendAclData(pConn, pData);
/* decrement available buffer count */
if (hciCoreCb.availBufs > 0)
{
hciCoreCb.availBufs--;
}
else
{
HCI_TRACE_WARN0("hciCoreSendAclData availBufs=0");
}
}
/*************************************************************************************************/
/*!
* \brief Service the TX data path.
*
* \param bufs Number of new buffers now available.
*
* \return None.
*/
/*************************************************************************************************/
void hciCoreTxReady(uint8_t bufs)
{
uint8_t *pData;
wsfHandlerId_t handlerId;
uint16_t handle;
uint16_t len;
hciCoreConn_t *pConn;
/* increment available buffers, with ceiling */
if (bufs > 0)
{
hciCoreCb.availBufs += bufs;
if (hciCoreCb.availBufs > hciCoreCb.numBufs)
{
hciCoreCb.availBufs = hciCoreCb.numBufs;
}
}
/* service ACL data queue and send as many buffers as we can */
while (hciCoreCb.availBufs > 0)
{
/* send continuation of any fragments first */
if (hciCoreTxAclContinue(NULL) == FALSE)
{
/* if no fragments then check for any queued ACL data */
if ((pData = WsfMsgDeq(&hciCoreCb.aclQueue, &handlerId)) != NULL)
{
/* parse handle and length */
BYTES_TO_UINT16(handle, pData);
BYTES_TO_UINT16(len, &pData[2]);
/* look up conn structure and send data */
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
{
hciCoreTxAclStart(pConn, len, pData);
}
/* handle not found, connection must be closed */
else
{
/* discard buffer */
WsfMsgFree(pData);
HCI_TRACE_WARN1("hciCoreTxReady discarding buffer, handle=%u", handle);
}
}
else
{
/* no fragments or queued data to send; we're done */
break;
}
}
}
}
/*************************************************************************************************/
/*!
* \brief Send ACL packets, start of packet.
*
* \param pConn Pointer to connection structure.
* \param len ACL packet length.
* \param pData WSF buffer containing an ACL packet.
*
* \return None.
*/
/*************************************************************************************************/
void hciCoreTxAclStart(hciCoreConn_t *pConn, uint16_t len, uint8_t *pData)
{
uint16_t hciLen;
/* make sure not already fragmenting on this connection */
WSF_ASSERT(pConn->fragmenting == FALSE);
hciLen = HciGetBufSize();
HCI_TRACE_INFO1("hciCoreTxAclStart len=%u", len);
/* if acl len > controller acl buf len */
if (len > hciLen)
{
/* store remaining acl len = acl len - hci acl buf len */
pConn->txAclRemLen = len - hciLen;
/* store position for next fragment */
pConn->pNextTxFrag = pData + hciLen;
/* store information required for fragmentation */
pConn->pTxAclPkt = pData;
pConn->fragmenting = TRUE;
/* set acl len in packet to hci acl buf len */
UINT16_TO_BUF(&pData[2], hciLen);
/* send the packet */
hciCoreSendAclData(pConn, pData);
/* send additional fragments while there are HCI buffers available */
while ((hciCoreCb.availBufs > 0) && hciCoreTxAclContinue(pConn));
}
else
{
/* no fragmentation, just send the packet */
hciCoreSendAclData(pConn, pData);
}
}
/*************************************************************************************************/
/*!
* \brief Send ACL packets, continuation of fragmented packets.
*
* \param pConn Pointer to connection structure. If set non-NULL, then a fragment is
* sent from this connection structure. If NULL the function finds the next
* connection structure with a fragment to be sent.
*
* \return TRUE if packet sent, FALSE otherwise.
*/
/*************************************************************************************************/
bool_t hciCoreTxAclContinue(hciCoreConn_t *pConn)
{
uint16_t aclLen;
if (pConn == NULL)
{
pConn = hciCoreNextConnFragment();
}
if (pConn != NULL)
{
/* get next fragment length */
aclLen = (pConn->txAclRemLen < HciGetBufSize()) ? pConn->txAclRemLen : HciGetBufSize();
if (aclLen > 0)
{
/* decrement remaining length */
pConn->txAclRemLen -= aclLen;
/* set handle in packet with continuation bit set */
UINT16_TO_BUF(pConn->pNextTxFrag, (pConn->handle | HCI_PB_CONTINUE));
/* set acl len in packet */
UINT16_TO_BUF(&(pConn->pNextTxFrag[2]), aclLen);
HCI_TRACE_INFO2("hciCoreTxAclContinue aclLen=%u remLen=%u", aclLen, pConn->txAclRemLen);
/* send the packet */
hciCoreSendAclData(pConn, pConn->pNextTxFrag);
/* set up pointer to next fragment */
if (pConn->txAclRemLen > 0)
{
pConn->pNextTxFrag += aclLen;
}
return TRUE;
}
}
return FALSE;
}
/*************************************************************************************************/
/*!
* \brief This function is called from the HCI transport layer when transmission of an ACL
* packet is complete.
*
* \param pConn Pointer to connection structure.
* \param pData WSF buffer containing an ACL packet.
*
* \return None.
*/
/*************************************************************************************************/
void hciCoreTxAclComplete(hciCoreConn_t *pConn, uint8_t *pData)
{
/* if fragmenting */
if (pConn->fragmenting)
{
/* check if all fragments sent */
if (pConn->txAclRemLen == 0)
{
/* free original buffer */
WsfMsgFree(pConn->pTxAclPkt);
pConn->pTxAclPkt = NULL;
pConn->fragmenting = FALSE;
HCI_TRACE_INFO0("hciCoreTxAclComplete free pTxAclPkt");
}
}
else if (pData != NULL)
{
WsfMsgFree(pData);
}
}
/*************************************************************************************************/
/*!
* \brief Reassemble an ACL packet.
*
* \param pData Input ACL packet.
*
* \return pointer to ACL packet to send, or NULL if no packet to send.
*/
/*************************************************************************************************/
uint8_t *hciCoreAclReassembly(uint8_t *pData)
{
hciCoreConn_t *pConn;
uint8_t *pDataRtn = NULL;
uint16_t handle;
uint16_t aclLen;
uint16_t l2cLen;
uint16_t pbf;
bool_t freeData = TRUE;
BYTES_TO_UINT16(handle, pData);
pbf = handle & HCI_PB_FLAG_MASK;
handle &= HCI_HANDLE_MASK;
BYTES_TO_UINT16(aclLen, &pData[2]);
/* look up connection */
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
{
/* if this is a start packet */
if (pbf == HCI_PB_START_C2H)
{
/* if currently reassembled packet not complete */
if (pConn->pRxAclPkt != NULL)
{
/* discard currently reassembled packet */
WsfMsgFree(pConn->pRxAclPkt);
pConn->pRxAclPkt = NULL;
HCI_TRACE_WARN1("disarded hci rx pkt handle=0x%04x", handle);
}
/* read l2cap length */
if (aclLen >= L2C_HDR_LEN)
{
BYTES_TO_UINT16(l2cLen, &pData[4]);
/* check length vs. configured maximum */
if ((l2cLen + L2C_HDR_LEN) > hciCoreCb.maxRxAclLen)
{
HCI_TRACE_WARN1("l2c len=0x%04x to large for reassembly", l2cLen);
}
/* if reassembly required */
else if ((l2cLen + L2C_HDR_LEN) > aclLen)
{
/* allocate buffer to store complete l2cap packet */
if ((pConn->pRxAclPkt = WsfMsgDataAlloc(l2cLen + L2C_HDR_LEN + HCI_ACL_HDR_LEN, 0)) != NULL)
{
/* store buffer for reassembly */
pConn->pNextRxFrag = pConn->pRxAclPkt;
/* build acl header and copy data */
UINT16_TO_BSTREAM(pConn->pNextRxFrag, handle);
UINT16_TO_BSTREAM(pConn->pNextRxFrag, l2cLen + L2C_HDR_LEN);
memcpy(pConn->pNextRxFrag, &pData[4], aclLen);
pConn->pNextRxFrag += aclLen;
/* store remaining length */
pConn->rxAclRemLen = l2cLen + L2C_HDR_LEN - aclLen;
}
else
{
/* alloc failed; discard */
HCI_TRACE_WARN1("reassembly alloc failed len=%u", (l2cLen + L2C_HDR_LEN + HCI_ACL_HDR_LEN));
}
}
else
{
/* no reassembly required, pData is ready to go */
pDataRtn = pData;
freeData = FALSE;
}
}
else
{
/* invalid l2cap packet; discard */
HCI_TRACE_WARN1("invalid l2c pkt aclLen=%u", aclLen);
}
}
/* else if this is a continuation packet */
else if (pbf == HCI_PB_CONTINUE)
{
/* if expecting a continuation */
if (pConn->pRxAclPkt != NULL)
{
if (aclLen <= pConn->rxAclRemLen)
{
/* copy data to start of next fragment */
memcpy(pConn->pNextRxFrag, &pData[HCI_ACL_HDR_LEN], aclLen);
pConn->pNextRxFrag += aclLen;
/* update remaining length */
pConn->rxAclRemLen -= aclLen;
/* if reassembly complete return reassembled packet */
if (pConn->rxAclRemLen == 0)
{
pDataRtn = pConn->pRxAclPkt;
pConn->pRxAclPkt = NULL;
}
}
else
{
HCI_TRACE_WARN2("continuation pkt too long len=%u RemLen=%u", aclLen, pConn->rxAclRemLen);
}
}
else
{
HCI_TRACE_WARN1("unexpected continuation pkt handle=0x%04x", handle);
}
}
/* else unknown packet type */
else
{
HCI_TRACE_WARN1("unknown pb flags=0x%04x", pbf);
}
}
else
{
/* connection not found */
HCI_TRACE_WARN1("pkt rcvd on unknown handle=0x%04x", (handle & HCI_HANDLE_MASK));
}
if (freeData)
{
WsfMsgFree(pData);
}
return pDataRtn;
}
/*************************************************************************************************/
/*!
* \brief Check if a TX ACL packet is being fragmented.
*
* \param pContext Connection context.
*
* \return TRUE if fragmenting a TX ACL packet, FALSE otherwise.
*/
/*************************************************************************************************/
bool_t hciCoreTxAclDataFragmented(hciCoreConn_t *pConn)
{
return pConn->fragmenting;
}
/*************************************************************************************************/
/*!
* \brief HCI core initialization.
*
* \return None.
*/
/*************************************************************************************************/
void HciCoreInit(void)
{
uint8_t i;
WSF_QUEUE_INIT(&hciCoreCb.aclQueue);
for (i = 0; i < DM_CONN_MAX; i++)
{
hciCoreCb.conn[i].handle = HCI_HANDLE_NONE;
}
hciCoreCb.maxRxAclLen = HCI_MAX_RX_ACL_LEN;
hciCoreCb.aclQueueHi = HCI_ACL_QUEUE_HI;
hciCoreCb.aclQueueLo = HCI_ACL_QUEUE_LO;
hciCoreCb.extResetSeq = NULL;
hciCoreInit();
}
/*************************************************************************************************/
/*!
* \brief Initiate an HCI reset sequence.
*
* \return None.
*/
/*************************************************************************************************/
void HciResetSequence(void)
{
/* set resetting state */
hciCb.resetting = TRUE;
/* start the reset sequence */
hciCoreResetStart();
}
/*************************************************************************************************/
/*!
* \brief Set the maximum reassembled RX ACL packet length. Minimum value is 27.
*
* \param len ACL packet length.
*
* \return None.
*/
/*************************************************************************************************/
void HciSetMaxRxAclLen(uint16_t len)
{
hciCoreCb.maxRxAclLen = len;
}
/*************************************************************************************************/
/*!
* \brief Set TX ACL queue high and low watermarks.
*
* \param queueHi Disable flow on a connection when this many ACL buffers are queued.
* queueLo Disable flow on a connection when this many ACL buffers are queued.
*
* \return None.
*/
/*************************************************************************************************/
void HciSetAclQueueWatermarks(uint8_t queueHi, uint8_t queueLo)
{
hciCoreCb.aclQueueHi = queueHi;
hciCoreCb.aclQueueLo = queueLo;
}
/*************************************************************************************************/
/*!
* \brief Set LE supported features configuration mask.
*
* \param feat Feature bit to set or clear
* \param flag TRUE to set feature bit and FALSE to clear it
*
* \return None.
*/
/*************************************************************************************************/
void HciSetLeSupFeat(uint32_t feat, bool_t flag)
{
/* if asked to include feature */
if (flag)
{
/* set feature bit */
hciLeSupFeatCfg |= feat;
}
else
{
/* clear feature bit */
hciLeSupFeatCfg &= ~feat;
}
}
/*************************************************************************************************/
/*!
* \brief Send data from the stack to HCI.
*
* \param pData WSF buffer containing an ACL packet
*
* \return None.
*/
/*************************************************************************************************/
void HciSendAclData(uint8_t *pData)
{
uint16_t handle;
uint16_t len;
hciCoreConn_t *pConn;
/* parse handle and length */
BYTES_TO_UINT16(handle, pData);
BYTES_TO_UINT16(len, &pData[2]);
/* look up connection structure */
if ((pConn = hciCoreConnByHandle(handle)) != NULL)
{
/* if queue empty and buffers available */
if (WsfQueueEmpty(&hciCoreCb.aclQueue) && hciCoreCb.availBufs > 0)
{
/* send data */
hciCoreTxAclStart(pConn, len, pData);
}
else
{
/* queue data - message handler ID 'handerId' not used */
WsfMsgEnq(&hciCoreCb.aclQueue, 0, pData);
}
/* increment buffer queue count for this connection with consideration for HCI fragmentation */
pConn->queuedBufs += ((len - 1) / HciGetBufSize()) + 1;
/* manage flow control to stack */
if (pConn->queuedBufs >= hciCoreCb.aclQueueHi && pConn->flowDisabled == FALSE)
{
pConn->flowDisabled = TRUE;
(*hciCb.flowCback)(handle, TRUE);
}
}
/* connection not found, connection must be closed */
else
{
/* discard buffer */
WsfMsgFree(pData);
HCI_TRACE_WARN1("HciSendAclData discarding buffer, handle=%u", handle);
}
}
@@ -0,0 +1,79 @@
/*************************************************************************************************/
/*!
* \file hci_tr.c
*
* \brief HCI transport module.
*
* Copyright (c) 2011-2018 Arm Ltd.
*
* 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 <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "util/bstream.h"
#include "hci_api.h"
#include "hci_core.h"
#include "hci_tr.h"
#include "ll_api.h"
/*************************************************************************************************/
/*!
* \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;
uint8_t *p;
/* if fragmenting */
if (hciCoreTxAclDataFragmented(pContext))
{
/* get 16-bit length */
BYTES_TO_UINT16(len, (pData + 2))
len += HCI_ACL_HDR_LEN;
/* allocate LL buffer */
if ((p = WsfMsgDataAlloc(len, HCI_TX_DATA_TAILROOM)) != NULL)
{
/* copy data */
memcpy(p, pData, len);
/* send to LL */
LlSendAclData(p);
/* free HCI buffer */
hciCoreTxAclComplete(pContext, pData);
}
}
else
{
/* send to LL */
LlSendAclData(pData);
/* LL will free HCI buffer */
hciCoreTxAclComplete(pContext, NULL);
}
}