blob: 3c638dad3eded2d2f9454bc101df2d5fc207002f [file] [log] [blame]
/**
****************************************************************************************
*
* @file data.c
*
* @brief FTDF data send/receive functions
*
* Copyright (c) 2016, Dialog Semiconductor
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
****************************************************************************************
*/
#include <stdlib.h>
#include <ftdf.h>
#include "internal.h"
#include "regmap.h"
#ifndef FTDF_LITE
void FTDF_processDataRequest(FTDF_DataRequest *dataRequest)
{
#ifndef FTDF_NO_TSCH
if (FTDF_pib.tschEnabled &&
FTDF_tschSlotLink->request != dataRequest)
{
FTDF_Status status;
if (dataRequest->dstAddrMode == FTDF_SHORT_ADDRESS &&
!dataRequest->indirectTX)
{
status = FTDF_scheduleTsch((FTDF_MsgBuffer *) dataRequest);
if (status == FTDF_SUCCESS)
{
return;
}
}
else
{
status = FTDF_INVALID_PARAMETER;
}
FTDF_sendDataConfirm(dataRequest,
status,
0,
0,
0,
NULL);
return;
}
#endif /* FTDF_NO_TSCH */
int queue;
FTDF_AddressMode dstAddrMode = dataRequest->dstAddrMode;
FTDF_PANId dstPANId = dataRequest->dstPANId;
FTDF_Address dstAddr = dataRequest->dstAddr;
// Search for an existing indirect queue
for (queue = 0; queue < FTDF_NR_OF_REQ_BUFFERS; queue++)
{
if (dstAddrMode == FTDF_SHORT_ADDRESS)
{
if (FTDF_txPendingList[ queue ].addrMode == dstAddrMode &&
FTDF_txPendingList[ queue ].addr.shortAddress == dstAddr.shortAddress)
{
break;
}
}
else if (dstAddrMode == FTDF_EXTENDED_ADDRESS)
{
if (FTDF_txPendingList[ queue ].addrMode == dstAddrMode &&
FTDF_txPendingList[ queue ].addr.extAddress == dstAddr.extAddress)
{
break;
}
}
}
if (dataRequest->indirectTX)
{
FTDF_Status status = FTDF_SUCCESS;
if (queue < FTDF_NR_OF_REQ_BUFFERS)
{
// Queue request in existing queue
status = FTDF_queueReqHead((FTDF_MsgBuffer *) dataRequest, &FTDF_txPendingList[ queue ].queue);
if (status == FTDF_SUCCESS)
{
FTDF_addTxPendingTimer((FTDF_MsgBuffer *) dataRequest,
queue,
FTDF_pib.transactionPersistenceTime * FTDF_BASE_SUPERFRAME_DURATION,
FTDF_sendTransactionExpired);
return;
}
}
if (dstAddrMode != FTDF_EXTENDED_ADDRESS &&
dstAddrMode != FTDF_SHORT_ADDRESS)
{
status = FTDF_INVALID_PARAMETER;
}
if (status != FTDF_SUCCESS)
{
// Queueing of indirect transfer was not successful
FTDF_sendDataConfirm(dataRequest,
status,
0,
0,
0,
NULL);
return;
}
#if FTDF_FP_BIT_MODE == FTDF_FP_BIT_MODE_AUTO
uint8_t entry, shortAddrIdx;
if (dstAddrMode == FTDF_SHORT_ADDRESS)
{
if (FTDF_fpprGetFreeShortAddress(&entry, &shortAddrIdx) == FTDF_FALSE)
{
goto transaction_overflow;
}
}
else if (dstAddrMode == FTDF_EXTENDED_ADDRESS)
{
if (FTDF_fpprGetFreeExtAddress(&entry) == FTDF_FALSE)
{
goto transaction_overflow;
}
}
else
{
status = FTDF_INVALID_PARAMETER;
}
#endif
// Search for an empty indirect queue
for (queue = 0; queue < FTDF_NR_OF_REQ_BUFFERS; queue++)
{
if (FTDF_txPendingList[ queue ].addrMode == FTDF_NO_ADDRESS)
{
FTDF_txPendingList[ queue ].addrMode = dstAddrMode;
FTDF_txPendingList[ queue ].PANId = dstPANId;
FTDF_txPendingList[ queue ].addr = dstAddr;
status = FTDF_queueReqHead((FTDF_MsgBuffer *) dataRequest,
&FTDF_txPendingList[ queue ].queue);
if (status == FTDF_SUCCESS)
{
#if FTDF_FP_BIT_MODE == FTDF_FP_BIT_MODE_AUTO
if (dstAddrMode == FTDF_SHORT_ADDRESS)
{
FTDF_fpprSetShortAddress(entry, shortAddrIdx, dstAddr.shortAddress);
FTDF_fpprSetShortAddressValid(entry, shortAddrIdx, FTDF_TRUE);
}
else if (dstAddrMode == FTDF_EXTENDED_ADDRESS)
{
FTDF_fpprSetExtAddress(entry, dstAddr.shortAddress);
FTDF_fpprSetExtAddressValid(entry, FTDF_TRUE);
}
else
{
ASSERT_WARNING(0);
}
#endif /* FTDF_FP_BIT_MODE == FTDF_FP_BIT_MODE_AUTO */
FTDF_addTxPendingTimer((FTDF_MsgBuffer *) dataRequest,
queue,
FTDF_pib.transactionPersistenceTime * FTDF_BASE_SUPERFRAME_DURATION,
FTDF_sendTransactionExpired);
return;
}
else
{
break;
}
}
}
// Did not find an existing or an empty queue
#if FTDF_FP_BIT_MODE == FTDF_FP_BIT_MODE_AUTO
transaction_overflow:
#endif
FTDF_sendDataConfirm(dataRequest, FTDF_TRANSACTION_OVERFLOW,
0,
0,
0,
NULL);
return;
}
if (FTDF_reqCurrent == NULL)
{
FTDF_reqCurrent = (FTDF_MsgBuffer *) dataRequest;
}
else
{
if (FTDF_queueReqHead((FTDF_MsgBuffer *) dataRequest, &FTDF_reqQueue) == FTDF_TRANSACTION_OVERFLOW)
{
FTDF_sendDataConfirm(dataRequest, FTDF_TRANSACTION_OVERFLOW,
0,
0,
0,
NULL);
}
return;
}
FTDF_FrameHeader *frameHeader = &FTDF_fh;
FTDF_SecurityHeader *securityHeader = &FTDF_sh;
frameHeader->frameType = dataRequest->sendMultiPurpose ? FTDF_MULTIPURPOSE_FRAME : FTDF_DATA_FRAME;
FTDF_Boolean framePending;
if (queue < FTDF_NR_OF_REQ_BUFFERS)
{
framePending = FTDF_TRUE;
}
else
{
framePending = FTDF_FALSE;
}
frameHeader->options =
(dataRequest->securityLevel > 0 ? FTDF_OPT_SECURITY_ENABLED : 0) |
(dataRequest->ackTX ? FTDF_OPT_ACK_REQUESTED : 0) |
(framePending ? FTDF_OPT_FRAME_PENDING : 0) |
((dataRequest->frameControlOptions & FTDF_PAN_ID_PRESENT) ? FTDF_OPT_PAN_ID_PRESENT : 0) |
((dataRequest->frameControlOptions & FTDF_IES_INCLUDED) ? FTDF_OPT_IES_PRESENT : 0) |
((dataRequest->frameControlOptions & FTDF_SEQ_NR_SUPPRESSED) ? FTDF_OPT_SEQ_NR_SUPPRESSED : 0);
if (FTDF_pib.leEnabled || FTDF_pib.tschEnabled)
{
frameHeader->options |= FTDF_OPT_ENHANCED;
}
frameHeader->srcAddrMode = dataRequest->srcAddrMode;
frameHeader->srcPANId = FTDF_pib.PANId;
frameHeader->dstAddrMode = dataRequest->dstAddrMode;
frameHeader->dstPANId = dataRequest->dstPANId;
frameHeader->dstAddr = dataRequest->dstAddr;
securityHeader->securityLevel = dataRequest->securityLevel;
securityHeader->keyIdMode = dataRequest->keyIdMode;
securityHeader->keyIndex = dataRequest->keyIndex;
securityHeader->keySource = dataRequest->keySource;
securityHeader->frameCounter = FTDF_pib.frameCounter;
securityHeader->frameCounterMode = FTDF_pib.frameCounterMode;
#ifndef FTDF_NO_TSCH
if (FTDF_pib.tschEnabled)
{
frameHeader->SN = FTDF_processTschSN((FTDF_MsgBuffer *)dataRequest,
FTDF_pib.DSN,
&dataRequest->requestSN);
}
else
#endif /* FTDF_NO_TSCH */
{
frameHeader->SN = FTDF_pib.DSN;
}
FTDF_Octet *txPtr = (FTDF_Octet *) FTDF_GET_REG_ADDR(RETENTION_RAM_TX_FIFO) +
(FTDF_BUFFER_LENGTH * FTDF_TX_DATA_BUFFER);
// Skip PHY header (= MAC length)
txPtr++;
FTDF_DataLength msduLength = dataRequest->msduLength;
txPtr = FTDF_addFrameHeader(txPtr,
frameHeader,
msduLength);
txPtr = FTDF_addSecurityHeader(txPtr,
securityHeader);
#if !defined(FTDF_NO_CSL) || !defined(FTDF_NO_TSCH)
if (dataRequest->frameControlOptions & FTDF_IES_INCLUDED)
{
txPtr = FTDF_addIes(txPtr,
dataRequest->headerIEList,
dataRequest->payloadIEList,
dataRequest->msduLength);
}
#endif /* !FTDF_NO_CSL || !FTDF_NO_TSCH */
FTDF_Status status = FTDF_sendFrame(FTDF_pib.currentChannel,
frameHeader,
securityHeader,
txPtr,
dataRequest->msduLength,
dataRequest->msdu);
if (status != FTDF_SUCCESS)
{
FTDF_sendDataConfirm(dataRequest, status,
0,
0,
0,
NULL);
FTDF_reqCurrent = NULL;
return;
}
FTDF_nrOfRetries = 0;
if (frameHeader->SN == FTDF_pib.DSN)
{
FTDF_pib.DSN++;
}
}
void FTDF_sendDataConfirm(FTDF_DataRequest *dataRequest,
FTDF_Status status,
FTDF_Time timestamp,
FTDF_SN DSN,
FTDF_NumOfBackoffs numOfBackoffs,
FTDF_IEList *ackPayload)
{
FTDF_REL_DATA_BUFFER(dataRequest->msdu);
FTDF_DataConfirm *dataConfirm = (FTDF_DataConfirm *) FTDF_GET_MSG_BUFFER(sizeof(FTDF_DataConfirm));
dataConfirm->msgId = FTDF_DATA_CONFIRM;
dataConfirm->msduHandle = dataRequest->msduHandle;
dataConfirm->status = status;
dataConfirm->timestamp = timestamp;
dataConfirm->numOfBackoffs = numOfBackoffs;
dataConfirm->DSN = DSN;
dataConfirm->ackPayload = ackPayload;
if (FTDF_reqCurrent == (FTDF_MsgBuffer *)dataRequest)
{
FTDF_reqCurrent = NULL;
}
FTDF_REL_MSG_BUFFER((FTDF_MsgBuffer *) dataRequest);
FTDF_RCV_MSG((FTDF_MsgBuffer *) dataConfirm);
#if FTDF_FP_BIT_MODE == FTDF_FP_BIT_MODE_AUTO
FTDF_fpFsmClearPending();
#endif /* FTDF_FP_BIT_MODE == FTDF_FP_BIT_MODE_AUTO */
FTDF_processNextRequest();
}
void FTDF_sendDataIndication(FTDF_FrameHeader *frameHeader,
FTDF_SecurityHeader *securityHeader,
FTDF_IEList *payloadIEList,
FTDF_DataLength msduLength,
FTDF_Octet *msdu,
FTDF_LinkQuality mpduLinkQuality,
FTDF_Time timestamp)
{
FTDF_DataIndication *dataIndication = (FTDF_DataIndication *) FTDF_GET_MSG_BUFFER(sizeof(FTDF_DataIndication));
FTDF_Octet *msduBuf = FTDF_GET_DATA_BUFFER(msduLength);
FTDF_Octet *bufPtr = msduBuf;
int n;
for (n = 0; n < msduLength; n++)
{
*msduBuf++ = *msdu++;
}
dataIndication->msgId = FTDF_DATA_INDICATION;
dataIndication->srcAddrMode = frameHeader->srcAddrMode;
dataIndication->srcPANId = frameHeader->srcPANId;
dataIndication->srcAddr = frameHeader->srcAddr;
dataIndication->dstAddrMode = frameHeader->dstAddrMode;
dataIndication->dstPANId = frameHeader->dstPANId;
dataIndication->dstAddr = frameHeader->dstAddr;
dataIndication->msduLength = msduLength;
dataIndication->msdu = bufPtr;
dataIndication->mpduLinkQuality = mpduLinkQuality;
dataIndication->DSN = frameHeader->SN;
dataIndication->timestamp = timestamp;
dataIndication->securityLevel = securityHeader->securityLevel;
dataIndication->keyIdMode = securityHeader->keyIdMode;
dataIndication->keyIndex = securityHeader->keyIndex;
dataIndication->payloadIEList = payloadIEList;
if (dataIndication->keyIdMode == 0x2)
{
for (n = 0; n < 4; n++)
{
dataIndication->keySource[ n ] = securityHeader->keySource[ n ];
}
}
else if (dataIndication->keyIdMode == 0x3)
{
for (n = 0; n < 8; n++)
{
dataIndication->keySource[ n ] = securityHeader->keySource[ n ];
}
}
FTDF_RCV_MSG((FTDF_MsgBuffer *) dataIndication);
}
void FTDF_processPollRequest(FTDF_PollRequest *pollRequest)
{
if (FTDF_reqCurrent == NULL)
{
FTDF_reqCurrent = (FTDF_MsgBuffer *) pollRequest;
}
else
{
if (FTDF_queueReqHead((FTDF_MsgBuffer *) pollRequest, &FTDF_reqQueue) == FTDF_TRANSACTION_OVERFLOW)
{
FTDF_sendPollConfirm(pollRequest, FTDF_TRANSACTION_OVERFLOW);
}
return;
}
FTDF_FrameHeader *frameHeader = &FTDF_fh;
FTDF_SecurityHeader *securityHeader = &FTDF_sh;
frameHeader->frameType = FTDF_MAC_COMMAND_FRAME;
frameHeader->options =
(pollRequest->securityLevel > 0 ? FTDF_OPT_SECURITY_ENABLED : 0) | FTDF_OPT_ACK_REQUESTED;
if (FTDF_pib.shortAddress < 0xfffe)
{
frameHeader->srcAddrMode = FTDF_SHORT_ADDRESS;
frameHeader->srcAddr.shortAddress = FTDF_pib.shortAddress;
}
else
{
frameHeader->srcAddrMode = FTDF_EXTENDED_ADDRESS;
frameHeader->srcAddr.extAddress = FTDF_pib.extAddress;
}
frameHeader->srcPANId = FTDF_pib.PANId;
frameHeader->dstAddrMode = pollRequest->coordAddrMode;
frameHeader->dstPANId = pollRequest->coordPANId;
frameHeader->dstAddr = pollRequest->coordAddr;
securityHeader->securityLevel = pollRequest->securityLevel;
securityHeader->keyIdMode = pollRequest->keyIdMode;
securityHeader->keyIndex = pollRequest->keyIndex;
securityHeader->keySource = pollRequest->keySource;
securityHeader->frameCounter = FTDF_pib.frameCounter;
securityHeader->frameCounterMode = FTDF_pib.frameCounterMode;
FTDF_Octet *txPtr = (FTDF_Octet *) FTDF_GET_REG_ADDR(RETENTION_RAM_TX_FIFO) +
(FTDF_BUFFER_LENGTH * FTDF_TX_DATA_BUFFER);
// Skip PHY header (= MAC length)
txPtr++;
frameHeader->SN = FTDF_pib.DSN;
txPtr = FTDF_addFrameHeader(txPtr,
frameHeader,
1);
txPtr = FTDF_addSecurityHeader(txPtr,
securityHeader);
*txPtr++ = FTDF_COMMAND_DATA_REQUEST;
FTDF_Status status = FTDF_sendFrame(FTDF_pib.currentChannel,
frameHeader,
securityHeader,
txPtr,
0,
NULL);
if (status != FTDF_SUCCESS)
{
FTDF_sendPollConfirm(pollRequest, status);
return;
}
FTDF_nrOfRetries = 0;
FTDF_pib.DSN++;
}
void FTDF_sendPollConfirm(FTDF_PollRequest *pollRequest, FTDF_Status status)
{
FTDF_PollConfirm *pollConfirm = (FTDF_PollConfirm *) FTDF_GET_MSG_BUFFER(sizeof(FTDF_PollConfirm));
pollConfirm->msgId = FTDF_POLL_CONFIRM;
pollConfirm->status = status;
if (FTDF_reqCurrent == (FTDF_MsgBuffer *)pollRequest)
{
FTDF_reqCurrent = NULL;
}
FTDF_REL_MSG_BUFFER((FTDF_MsgBuffer *) pollRequest);
FTDF_RCV_MSG((FTDF_MsgBuffer *) pollConfirm);
FTDF_processNextRequest();
}
void FTDF_processPurgeRequest(FTDF_PurgeRequest *purgeRequest)
{
FTDF_Handle msduHandle = purgeRequest->msduHandle;
FTDF_Status status = FTDF_INVALID_HANDLE;
int n;
for (n = 0; n < FTDF_NR_OF_REQ_BUFFERS; n++)
{
FTDF_MsgBuffer *request = FTDF_dequeueByHandle(msduHandle, &FTDF_txPendingList[ n ].queue);
if (request)
{
FTDF_DataRequest *dataRequest = (FTDF_DataRequest *) request;
if (dataRequest->indirectTX == FTDF_TRUE)
{
FTDF_removeTxPendingTimer(request);
#if FTDF_FP_BIT_MODE == FTDF_FP_BIT_MODE_AUTO
if (FTDF_txPendingList[ n ].addrMode == FTDF_SHORT_ADDRESS)
{
uint8_t entry, shortAddrIdx;
FTDF_Boolean found = FTDF_fpprLookupShortAddress(
FTDF_txPendingList[ n ].addr.shortAddress, &entry,
&shortAddrIdx);
ASSERT_WARNING(found);
FTDF_fpprSetShortAddressValid(entry, shortAddrIdx, FTDF_FALSE);
}
else if (FTDF_txPendingList[ n ].addrMode == FTDF_EXTENDED_ADDRESS)
{
uint8_t entry;
FTDF_Boolean found = FTDF_fpprLookupExtAddress(
FTDF_txPendingList[ n ].addr.extAddress, &entry);
ASSERT_WARNING(found);
FTDF_fpprSetExtAddressValid(entry, FTDF_FALSE);
}
else
{
ASSERT_WARNING(0);
}
#endif /* FTDF_FP_BIT_MODE == FTDF_FP_BIT_MODE_AUTO */
if (FTDF_isQueueEmpty(&FTDF_txPendingList[ n ].queue))
{
FTDF_txPendingList[ n ].addrMode = FTDF_NO_ADDRESS;
}
}
FTDF_REL_DATA_BUFFER(dataRequest->msdu);
FTDF_REL_MSG_BUFFER((FTDF_MsgBuffer *) dataRequest);
status = FTDF_SUCCESS;
break;
}
}
FTDF_PurgeConfirm *purgeConfirm = (FTDF_PurgeConfirm *) FTDF_GET_MSG_BUFFER(sizeof(FTDF_PurgeConfirm));
purgeConfirm->msgId = FTDF_PURGE_CONFIRM;
purgeConfirm->msduHandle = msduHandle;
purgeConfirm->status = status;
FTDF_REL_MSG_BUFFER((FTDF_MsgBuffer *) purgeRequest);
FTDF_RCV_MSG((FTDF_MsgBuffer *) purgeConfirm);
}
#endif /* !FTDF_LITE */
#ifdef FTDF_PHY_API
FTDF_Status FTDF_sendFrameSimple(FTDF_DataLength frameLength,
FTDF_Octet *frame,
FTDF_ChannelNumber channel,
FTDF_PTI pti,
FTDF_Boolean csmaSuppress)
{
FTDF_Octet *fp = frame;
if (frameLength > 127 ||
FTDF_transparentMode != FTDF_TRUE)
{
return FTDF_INVALID_PARAMETER;
}
FTDF_Octet *txPtr = (FTDF_Octet *) FTDF_GET_REG_ADDR(RETENTION_RAM_TX_FIFO) +
(FTDF_BUFFER_LENGTH * FTDF_TX_DATA_BUFFER);
/* This data copy could/should be optimized using DMA */
*txPtr++ = frameLength;
int n;
for (n = 0; n < frameLength; n++)
{
*txPtr++ = *fp++;
}
FTDF_criticalVar();
FTDF_enterCritical();
FTDF_nrOfRetries = 0;
FTDF_exitCritical();
FTDF_sendTransparentFrame(frameLength,
frame,
channel,
pti,
csmaSuppress);
return FTDF_SUCCESS;
}
#else /* !FTDF_PHY_API */
void FTDF_processTransparentRequest(FTDF_TransparentRequest *transparentRequest)
{
FTDF_DataLength frameLength = transparentRequest->frameLength;
if (frameLength > 127 ||
FTDF_transparentMode != FTDF_TRUE)
{
FTDF_SEND_FRAME_TRANSPARENT_CONFIRM(transparentRequest->handle,
FTDF_INVALID_PARAMETER);
FTDF_REL_MSG_BUFFER((FTDF_MsgBuffer *) transparentRequest);
return;
}
if (FTDF_reqCurrent == NULL)
{
FTDF_reqCurrent = (FTDF_MsgBuffer *) transparentRequest;
}
else
{
#ifndef FTDF_LITE
if (FTDF_queueReqHead((FTDF_MsgBuffer *) transparentRequest, &FTDF_reqQueue) == FTDF_TRANSACTION_OVERFLOW)
{
#endif /* !FTDF_LITE */
FTDF_SEND_FRAME_TRANSPARENT_CONFIRM(transparentRequest->handle,
FTDF_TRANSPARENT_OVERFLOW);
FTDF_REL_MSG_BUFFER((FTDF_MsgBuffer *) transparentRequest);
#ifndef FTDF_LITE
}
#endif /* !FTDF_LITE */
return;
}
FTDF_Octet *txPtr = (FTDF_Octet *) FTDF_GET_REG_ADDR(RETENTION_RAM_TX_FIFO) +
(FTDF_BUFFER_LENGTH * FTDF_TX_DATA_BUFFER);
*txPtr++ = frameLength;
FTDF_Octet *frame = transparentRequest->frame;
int n;
for (n = 0; n < frameLength; n++)
{
*txPtr++ = *frame++;
}
FTDF_sendTransparentFrame(frameLength,
transparentRequest->frame,
transparentRequest->channel,
transparentRequest->pti,
transparentRequest->cmsaSuppress);
FTDF_nrOfRetries = 0;
}
void FTDF_sendFrameTransparent(FTDF_DataLength frameLength,
FTDF_Octet *frame,
FTDF_ChannelNumber channel,
FTDF_PTI pti,
FTDF_Boolean cmsaSuppress,
void *handle)
{
FTDF_TransparentRequest *transparentRequest =
(FTDF_TransparentRequest *) FTDF_GET_MSG_BUFFER(sizeof(FTDF_TransparentRequest));
transparentRequest->msgId = FTDF_TRANSPARENT_REQUEST;
transparentRequest->frameLength = frameLength;
transparentRequest->frame = frame;
transparentRequest->channel = channel;
transparentRequest->pti = pti;
transparentRequest->cmsaSuppress = cmsaSuppress;
transparentRequest->handle = handle;
FTDF_processTransparentRequest(transparentRequest);
}
#endif /* FTDF_PHY_API */