blob: 1f662169d993e528b5047a1cc76ffde54c0ba59a [file] [log] [blame]
/*
* Copyright (C) 2010 NXP Semiconductors
*
* 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.
*/
/**
* \file phFriNfc_Llcp.c
* \brief NFC LLCP core
*
* Project: NFC-FRI
*
*/
/*include files*/
#include <phNfcLlcpTypes.h>
#include <phOsalNfc_Timer.h>
#include <phFriNfc_Llcp.h>
#include <phFriNfc_LlcpUtils.h>
/**
* \internal
* \name States of the LLC state machine.
*
*/
/*@{*/
#define PHFRINFC_LLCP_STATE_RESET_INIT 0 /**< \internal Initial state.*/
#define PHFRINFC_LLCP_STATE_CHECKED 1 /**< \internal The tag has been checked for LLCP compliance.*/
#define PHFRINFC_LLCP_STATE_ACTIVATION 2 /**< \internal The deactivation phase.*/
#define PHFRINFC_LLCP_STATE_PAX 3 /**< \internal Parameter exchange phase.*/
#define PHFRINFC_LLCP_STATE_OPERATION_RECV 4 /**< \internal Normal operation phase (ready to receive).*/
#define PHFRINFC_LLCP_STATE_OPERATION_SEND 5 /**< \internal Normal operation phase (ready to send).*/
#define PHFRINFC_LLCP_STATE_DEACTIVATION 6 /**< \internal The deactivation phase.*/
/*@}*/
/**
* \internal
* \name Masks used for VERSION parsing.
*
*/
/*@{*/
#define PHFRINFC_LLCP_VERSION_MAJOR_MASK 0xF0 /**< \internal Mask to apply to get major version number.*/
#define PHFRINFC_LLCP_VERSION_MINOR_MASK 0x0F /**< \internal Mask to apply to get major version number.*/
/*@}*/
/**
* \internal
* \name Invalid values for parameters.
*
*/
/*@{*/
#define PHFRINFC_LLCP_INVALID_VERSION 0x00 /**< \internal Invalid VERSION value.*/
/*@}*/
/**
* \internal
* \name Internal constants.
*
*/
/*@{*/
#define PHFRINFC_LLCP_MAX_PARAM_TLV_LENGTH \
(( PHFRINFC_LLCP_TLV_LENGTH_HEADER + PHFRINFC_LLCP_TLV_LENGTH_VERSION ) + \
( PHFRINFC_LLCP_TLV_LENGTH_HEADER + PHFRINFC_LLCP_TLV_LENGTH_MIUX ) + \
( PHFRINFC_LLCP_TLV_LENGTH_HEADER + PHFRINFC_LLCP_TLV_LENGTH_WKS ) + \
( PHFRINFC_LLCP_TLV_LENGTH_HEADER + PHFRINFC_LLCP_TLV_LENGTH_LTO ) + \
( PHFRINFC_LLCP_TLV_LENGTH_HEADER + PHFRINFC_LLCP_TLV_LENGTH_OPT )) /**< \internal Maximum size of link params TLV.*/
/*@}*/
/* --------------------------- Internal functions ------------------------------ */
static void phFriNfc_Llcp_Receive_CB( void *pContext,
NFCSTATUS status,
phNfc_sData_t *psData);
static NFCSTATUS phFriNfc_Llcp_HandleIncomingPacket( phFriNfc_Llcp_t *Llcp,
phNfc_sData_t *psPacket );
static void phFriNfc_Llcp_ResetLTO( phFriNfc_Llcp_t *Llcp );
static NFCSTATUS phFriNfc_Llcp_InternalSend( phFriNfc_Llcp_t *Llcp,
phFriNfc_Llcp_sPacketHeader_t *psHeader,
phFriNfc_Llcp_sPacketSequence_t *psSequence,
phNfc_sData_t *psInfo );
static bool_t phFriNfc_Llcp_HandlePendingSend ( phFriNfc_Llcp_t *Llcp );
static phNfc_sData_t * phFriNfc_Llcp_AllocateAndCopy(phNfc_sData_t * pOrig)
{
phNfc_sData_t * pDest = NULL;
if (pOrig == NULL)
{
return NULL;
}
pDest = phOsalNfc_GetMemory(sizeof(phNfc_sData_t));
if (pDest == NULL)
{
goto error;
}
pDest->buffer = phOsalNfc_GetMemory(pOrig->length);
if (pDest->buffer == NULL)
{
goto error;
}
memcpy(pDest->buffer, pOrig->buffer, pOrig->length);
pDest->length = pOrig->length;
return pDest;
error:
if (pDest != NULL)
{
if (pDest->buffer != NULL)
{
phOsalNfc_FreeMemory(pDest->buffer);
}
phOsalNfc_FreeMemory(pDest);
}
return NULL;
}
static void phFriNfc_Llcp_Deallocate(phNfc_sData_t * pData)
{
if (pData != NULL)
{
if (pData->buffer != NULL)
{
phOsalNfc_FreeMemory(pData->buffer);
}
else
{
LLCP_PRINT("Warning, deallocating empty buffer");
}
phOsalNfc_FreeMemory(pData);
}
}
static NFCSTATUS phFriNfc_Llcp_InternalDeactivate( phFriNfc_Llcp_t *Llcp )
{
phFriNfc_Llcp_Send_CB_t pfSendCB;
void * pSendContext;
if ((Llcp->state == PHFRINFC_LLCP_STATE_OPERATION_RECV) ||
(Llcp->state == PHFRINFC_LLCP_STATE_OPERATION_SEND) ||
(Llcp->state == PHFRINFC_LLCP_STATE_PAX) ||
(Llcp->state == PHFRINFC_LLCP_STATE_ACTIVATION))
{
/* Update state */
Llcp->state = PHFRINFC_LLCP_STATE_DEACTIVATION;
/* Stop timer */
phOsalNfc_Timer_Stop(Llcp->hSymmTimer);
/* Return delayed send operation in error, in any */
if (Llcp->psSendInfo != NULL)
{
phFriNfc_Llcp_Deallocate(Llcp->psSendInfo);
Llcp->psSendInfo = NULL;
Llcp->psSendHeader = NULL;
Llcp->psSendSequence = NULL;
}
if (Llcp->pfSendCB != NULL)
{
/* Get Callback params */
pfSendCB = Llcp->pfSendCB;
pSendContext = Llcp->pSendContext;
/* Reset callback params */
Llcp->pfSendCB = NULL;
Llcp->pSendContext = NULL;
/* Call the callback */
(pfSendCB)(pSendContext, NFCSTATUS_FAILED);
}
/* Notify service layer */
Llcp->pfLink_CB(Llcp->pLinkContext, phFriNfc_LlcpMac_eLinkDeactivated);
/* Forward check request to MAC layer */
return phFriNfc_LlcpMac_Deactivate(&Llcp->MAC);
}
return NFCSTATUS_SUCCESS;
}
static NFCSTATUS phFriNfc_Llcp_SendSymm( phFriNfc_Llcp_t *Llcp )
{
phFriNfc_Llcp_sPacketHeader_t sHeader;
sHeader.dsap = PHFRINFC_LLCP_SAP_LINK;
sHeader.ssap = PHFRINFC_LLCP_SAP_LINK;
sHeader.ptype = PHFRINFC_LLCP_PTYPE_SYMM;
return phFriNfc_Llcp_InternalSend(Llcp, &sHeader, NULL, NULL);
}
static NFCSTATUS phFriNfc_Llcp_SendPax( phFriNfc_Llcp_t *Llcp, phFriNfc_Llcp_sLinkParameters_t *psLinkParams)
{
uint8_t pTLVBuffer[PHFRINFC_LLCP_MAX_PARAM_TLV_LENGTH];
phNfc_sData_t sParamsTLV;
phFriNfc_Llcp_sPacketHeader_t sHeader;
NFCSTATUS result;
/* Prepare link parameters TLV */
sParamsTLV.buffer = pTLVBuffer;
sParamsTLV.length = PHFRINFC_LLCP_MAX_PARAM_TLV_LENGTH;
result = phFriNfc_Llcp_EncodeLinkParams(&sParamsTLV, psLinkParams, PHFRINFC_LLCP_VERSION);
if (result != NFCSTATUS_SUCCESS)
{
/* Error while encoding */
return NFCSTATUS_FAILED;
}
/* Check if ready to send */
if (Llcp->state != PHFRINFC_LLCP_STATE_OPERATION_SEND)
{
/* No send pending, send the PAX packet */
sHeader.dsap = PHFRINFC_LLCP_SAP_LINK;
sHeader.ssap = PHFRINFC_LLCP_SAP_LINK;
sHeader.ptype = PHFRINFC_LLCP_PTYPE_PAX;
return phFriNfc_Llcp_InternalSend(Llcp, &sHeader, NULL, &sParamsTLV);
}
else
{
/* Error: A send is already pending, cannot send PAX */
/* NOTE: this should not happen since PAX are sent before any other packet ! */
return NFCSTATUS_FAILED;
}
}
static NFCSTATUS phFriNfc_Llcp_SendDisconnect( phFriNfc_Llcp_t *Llcp )
{
phFriNfc_Llcp_sPacketHeader_t sHeader;
/* Check if ready to send */
if (Llcp->state != PHFRINFC_LLCP_STATE_OPERATION_SEND)
{
/* No send pending, send the DISC packet */
sHeader.dsap = PHFRINFC_LLCP_SAP_LINK;
sHeader.ssap = PHFRINFC_LLCP_SAP_LINK;
sHeader.ptype = PHFRINFC_LLCP_PTYPE_DISC;
return phFriNfc_Llcp_InternalSend(Llcp, &sHeader, NULL, NULL);
}
else
{
/* A send is already pending, raise a flag to send DISC as soon as possible */
Llcp->bDiscPendingFlag = TRUE;
return NFCSTATUS_PENDING;
}
}
static void phFriNfc_Llcp_Timer_CB(uint32_t TimerId, void *pContext)
{
phFriNfc_Llcp_t *Llcp = (phFriNfc_Llcp_t*)pContext;
PHNFC_UNUSED_VARIABLE(TimerId);
/* Check current state */
if (Llcp->state == PHFRINFC_LLCP_STATE_OPERATION_RECV)
{
/* No data is coming before LTO, disconnecting */
phFriNfc_Llcp_InternalDeactivate(Llcp);
}
else if (Llcp->state == PHFRINFC_LLCP_STATE_OPERATION_SEND)
{
/* Send SYMM */
phFriNfc_Llcp_SendSymm(Llcp);
}
else
{
/* Nothing to do if not in Normal Operation state */
}
}
static NFCSTATUS phFriNfc_Llcp_HandleAggregatedPacket( phFriNfc_Llcp_t *Llcp,
phNfc_sData_t *psRawPacket )
{
phNfc_sData_t sInfo;
phNfc_sData_t sCurrentInfo;
uint16_t length;
NFCSTATUS status;
/* Get info field */
sInfo.buffer = psRawPacket->buffer + PHFRINFC_LLCP_PACKET_HEADER_SIZE;
sInfo.length = psRawPacket->length - PHFRINFC_LLCP_PACKET_HEADER_SIZE;
/* Check for empty info field */
if (sInfo.length == 0)
{
return NFCSTATUS_FAILED;
}
/* Check consistency */
while (sInfo.length != 0)
{
/* Check if enough room to read length */
if (sInfo.length < sizeof(sInfo.length))
{
return NFCSTATUS_FAILED;
}
/* Read length */
length = (sInfo.buffer[0] << 8) | sInfo.buffer[1];
/* Update info buffer */
sInfo.buffer += 2; /*Size of length field is 2*/
sInfo.length -= 2; /*Size of length field is 2*/
/* Check if declared length fits in remaining space */
if (length > sInfo.length)
{
return NFCSTATUS_FAILED;
}
/* Update info buffer */
sInfo.buffer += length;
sInfo.length -= length;
}
/* Get info field */
sInfo.buffer = psRawPacket->buffer + PHFRINFC_LLCP_PACKET_HEADER_SIZE;
sInfo.length = psRawPacket->length - PHFRINFC_LLCP_PACKET_HEADER_SIZE;
/* Handle aggregated packets */
while (sInfo.length != 0)
{
/* Read length */
length = (sInfo.buffer[0] << 8) | sInfo.buffer[1];
/* Update info buffer */
sInfo.buffer += 2; /* Size of length field is 2 */
sInfo.length -= 2; /*Size of length field is 2*/
/* Handle aggregated packet */
sCurrentInfo.buffer=sInfo.buffer;
sCurrentInfo.length=length;
status = phFriNfc_Llcp_HandleIncomingPacket(Llcp, &sCurrentInfo);
if ( (status != NFCSTATUS_SUCCESS) &&
(status != NFCSTATUS_PENDING) )
{
/* TODO: Error: invalid frame */
}
/* Update info buffer */
sInfo.buffer += length;
sInfo.length -= length;
}
return NFCSTATUS_SUCCESS;
}
static NFCSTATUS phFriNfc_Llcp_ParseLinkParams( phNfc_sData_t *psParamsTLV,
phFriNfc_Llcp_sLinkParameters_t *psParsedParams,
uint8_t *pnParsedVersion )
{
NFCSTATUS status;
uint8_t type;
phFriNfc_Llcp_sLinkParameters_t sParams;
phNfc_sData_t sValueBuffer;
uint32_t offset = 0;
uint8_t version = PHFRINFC_LLCP_INVALID_VERSION;
/* Check for NULL pointers */
if ((psParamsTLV == NULL) || (psParsedParams == NULL) || (pnParsedVersion == NULL))
{
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Prepare default param structure */
sParams.miu = PHFRINFC_LLCP_MIU_DEFAULT;
sParams.wks = PHFRINFC_LLCP_WKS_DEFAULT;
sParams.lto = PHFRINFC_LLCP_LTO_DEFAULT;
sParams.option = PHFRINFC_LLCP_OPTION_DEFAULT;
/* Decode TLV */
while (offset < psParamsTLV->length)
{
status = phFriNfc_Llcp_DecodeTLV(psParamsTLV, &offset, &type, &sValueBuffer);
if (status != NFCSTATUS_SUCCESS)
{
/* Error: Ill-formed TLV */
return status;
}
switch(type)
{
case PHFRINFC_LLCP_TLV_TYPE_VERSION:
{
/* Check length */
if (sValueBuffer.length != PHFRINFC_LLCP_TLV_LENGTH_VERSION)
{
/* Error : Ill-formed VERSION parameter TLV */
break;
}
/* Get VERSION */
version = sValueBuffer.buffer[0];
break;
}
case PHFRINFC_LLCP_TLV_TYPE_MIUX:
{
/* Check length */
if (sValueBuffer.length != PHFRINFC_LLCP_TLV_LENGTH_MIUX)
{
/* Error : Ill-formed MIUX parameter TLV */
break;
}
/* Get MIU */
sParams.miu = (PHFRINFC_LLCP_MIU_DEFAULT + ((sValueBuffer.buffer[0] << 8) | sValueBuffer.buffer[1])) & PHFRINFC_LLCP_TLV_MIUX_MASK;
break;
}
case PHFRINFC_LLCP_TLV_TYPE_WKS:
{
/* Check length */
if (sValueBuffer.length != PHFRINFC_LLCP_TLV_LENGTH_WKS)
{
/* Error : Ill-formed MIUX parameter TLV */
break;
}
/* Get WKS */
sParams.wks = (sValueBuffer.buffer[0] << 8) | sValueBuffer.buffer[1];
/* Ignored bits must always be set */
sParams.wks |= PHFRINFC_LLCP_TLV_WKS_MASK;
break;
}
case PHFRINFC_LLCP_TLV_TYPE_LTO:
{
/* Check length */
if (sValueBuffer.length != PHFRINFC_LLCP_TLV_LENGTH_LTO)
{
/* Error : Ill-formed LTO parameter TLV */
break;
}
/* Get LTO */
sParams.lto = sValueBuffer.buffer[0];
break;
}
case PHFRINFC_LLCP_TLV_TYPE_OPT:
{
/* Check length */
if (sValueBuffer.length != PHFRINFC_LLCP_TLV_LENGTH_OPT)
{
/* Error : Ill-formed OPT parameter TLV */
break;;
}
/* Get OPT */
sParams.option = sValueBuffer.buffer[0] & PHFRINFC_LLCP_TLV_OPT_MASK;
break;
}
default:
{
/* Error : Unknown Type */
break;
}
}
}
/* Check if a VERSION parameter has been provided */
if (version == PHFRINFC_LLCP_INVALID_VERSION)
{
/* Error : Mandatory VERSION parameter not provided */
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Save response */
*pnParsedVersion = version;
memcpy(psParsedParams, &sParams, sizeof(phFriNfc_Llcp_sLinkParameters_t));
return NFCSTATUS_SUCCESS;
}
static NFCSTATUS phFriNfc_Llcp_VersionAgreement( uint8_t localVersion,
uint8_t remoteVersion,
uint8_t *pNegociatedVersion )
{
uint8_t localMajor = localVersion & PHFRINFC_LLCP_VERSION_MAJOR_MASK;
uint8_t localMinor = localVersion & PHFRINFC_LLCP_VERSION_MINOR_MASK;
uint8_t remoteMajor = remoteVersion & PHFRINFC_LLCP_VERSION_MAJOR_MASK;
uint8_t remoteMinor = remoteVersion & PHFRINFC_LLCP_VERSION_MINOR_MASK;
uint8_t negociatedVersion;
/* Check for NULL pointers */
if (pNegociatedVersion == NULL)
{
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Compare Major numbers */
if (localMajor == remoteMajor)
{
/* Version agreement succeed : use lowest version */
negociatedVersion = localMajor | ((remoteMinor<localMinor)?remoteMinor:localMinor);
}
else if (localMajor > remoteMajor)
{
/* Decide if versions are compatible */
/* Currently, there is no backward compatibility to handle */
return NFCSTATUS_FAILED;
}
else /* if (localMajor < remoteMajor) */
{
/* It is up to the remote host to decide if versions are compatible */
/* Set negociated version to our local version, the remote will
deacivate the link if its own version agreement fails */
negociatedVersion = localVersion;
}
/* Save response */
*pNegociatedVersion = negociatedVersion;
return NFCSTATUS_SUCCESS;
}
static NFCSTATUS phFriNfc_Llcp_InternalActivate( phFriNfc_Llcp_t *Llcp,
phNfc_sData_t *psParamsTLV)
{
NFCSTATUS status;
phFriNfc_Llcp_sLinkParameters_t sRemoteParams;
uint8_t remoteVersion;
uint8_t negociatedVersion;
const uint16_t nMaxHeaderSize = PHFRINFC_LLCP_PACKET_HEADER_SIZE +
PHFRINFC_LLCP_PACKET_SEQUENCE_SIZE;
/* Parse parameters */
status = phFriNfc_Llcp_ParseLinkParams(psParamsTLV, &sRemoteParams, &remoteVersion);
if (status != NFCSTATUS_SUCCESS)
{
/* Error: invalid parameters TLV */
status = NFCSTATUS_FAILED;
}
else
{
/* Version agreement procedure */
status = phFriNfc_Llcp_VersionAgreement(PHFRINFC_LLCP_VERSION , remoteVersion, &negociatedVersion);
if (status != NFCSTATUS_SUCCESS)
{
/* Error: version agreement failed */
status = NFCSTATUS_FAILED;
}
else
{
/* Save parameters */
Llcp->version = negociatedVersion;
memcpy(&Llcp->sRemoteParams, &sRemoteParams, sizeof(phFriNfc_Llcp_sLinkParameters_t));
/* Update remote MIU to match local Tx buffer size */
if (Llcp->nTxBufferLength < (Llcp->sRemoteParams.miu + nMaxHeaderSize))
{
Llcp->sRemoteParams.miu = Llcp->nTxBufferLength - nMaxHeaderSize;
}
/* Initiate Symmetry procedure by resetting LTO timer */
/* NOTE: this also updates current state */
phFriNfc_Llcp_ResetLTO(Llcp);
}
}
/* Notify upper layer, if Activation failed CB called by Deactivate */
if (status == NFCSTATUS_SUCCESS)
{
/* Link activated ! */
Llcp->pfLink_CB(Llcp->pLinkContext, phFriNfc_LlcpMac_eLinkActivated);
}
return status;
}
static NFCSTATUS phFriNfc_Llcp_HandleMACLinkActivated( phFriNfc_Llcp_t *Llcp,
phNfc_sData_t *psParamsTLV)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Create the timer */
Llcp->hSymmTimer = phOsalNfc_Timer_Create();
if (Llcp->hSymmTimer == PH_OSALNFC_INVALID_TIMER_ID)
{
/* Error: unable to create timer */
return NFCSTATUS_INSUFFICIENT_RESOURCES;
}
/* Check if params received from MAC activation procedure */
if (psParamsTLV == NULL)
{
/* No params with selected MAC mapping, enter PAX mode for parameter exchange */
Llcp->state = PHFRINFC_LLCP_STATE_PAX;
/* Set default MIU for PAX exchange */
Llcp->sRemoteParams.miu = PHFRINFC_LLCP_MIU_DEFAULT;
/* If the local device is the initiator, it must initiate PAX exchange */
if (Llcp->eRole == phFriNfc_LlcpMac_ePeerTypeInitiator)
{
/* Send PAX */
status = phFriNfc_Llcp_SendPax(Llcp, &Llcp->sLocalParams);
}
}
else
{
/* Params exchanged during MAX activation, try LLC activation */
status = phFriNfc_Llcp_InternalActivate(Llcp, psParamsTLV);
}
if (status == NFCSTATUS_SUCCESS)
{
/* Start listening for incoming packets */
Llcp->sRxBuffer.length = Llcp->nRxBufferLength;
phFriNfc_LlcpMac_Receive(&Llcp->MAC, &Llcp->sRxBuffer, phFriNfc_Llcp_Receive_CB, Llcp);
}
return status;
}
static void phFriNfc_Llcp_HandleMACLinkDeactivated( phFriNfc_Llcp_t *Llcp )
{
uint8_t state = Llcp->state;
/* Delete the timer */
if (Llcp->hSymmTimer != PH_OSALNFC_INVALID_TIMER_ID)
{
phOsalNfc_Timer_Delete(Llcp->hSymmTimer);
}
/* Reset state */
Llcp->state = PHFRINFC_LLCP_STATE_DEACTIVATION;
switch (state)
{
case PHFRINFC_LLCP_STATE_DEACTIVATION:
{
/* The service layer has already been notified, nothing more to do */
break;
}
default:
{
/* Notify service layer of link failure */
Llcp->pfLink_CB(Llcp->pLinkContext, phFriNfc_LlcpMac_eLinkDeactivated);
break;
}
}
}
static void phFriNfc_Llcp_ChkLlcp_CB( void *pContext,
NFCSTATUS status )
{
/* Get monitor from context */
phFriNfc_Llcp_t *Llcp = (phFriNfc_Llcp_t*)pContext;
/* Update state */
Llcp->state = PHFRINFC_LLCP_STATE_CHECKED;
/* Invoke callback */
Llcp->pfChk_CB(Llcp->pChkContext, status);
}
static void phFriNfc_Llcp_LinkStatus_CB( void *pContext,
phFriNfc_LlcpMac_eLinkStatus_t eLinkStatus,
phNfc_sData_t *psParamsTLV,
phFriNfc_LlcpMac_ePeerType_t PeerRemoteDevType)
{
NFCSTATUS status;
/* Get monitor from context */
phFriNfc_Llcp_t *Llcp = (phFriNfc_Llcp_t*)pContext;
/* Save the local peer role (initiator/target) */
Llcp->eRole = PeerRemoteDevType;
/* Check new link status */
switch(eLinkStatus)
{
case phFriNfc_LlcpMac_eLinkActivated:
{
/* Handle MAC link activation */
status = phFriNfc_Llcp_HandleMACLinkActivated(Llcp, psParamsTLV);
if (status != NFCSTATUS_SUCCESS)
{
/* Error: LLC link activation procedure failed, deactivate MAC link */
status = phFriNfc_Llcp_InternalDeactivate(Llcp);
}
break;
}
case phFriNfc_LlcpMac_eLinkDeactivated:
{
/* Handle MAC link deactivation (cannot fail) */
phFriNfc_Llcp_HandleMACLinkDeactivated(Llcp);
break;
}
default:
{
/* Warning: Unknown link status, should not happen */
}
}
}
static void phFriNfc_Llcp_ResetLTO( phFriNfc_Llcp_t *Llcp )
{
uint32_t nDuration;
/* Stop timer */
phOsalNfc_Timer_Stop(Llcp->hSymmTimer);
/* Update state */
if (Llcp->state == PHFRINFC_LLCP_STATE_OPERATION_RECV)
{
Llcp->state = PHFRINFC_LLCP_STATE_OPERATION_SEND;
}
else if (Llcp->state == PHFRINFC_LLCP_STATE_OPERATION_SEND)
{
Llcp->state = PHFRINFC_LLCP_STATE_OPERATION_RECV;
}
else if (Llcp->state != PHFRINFC_LLCP_STATE_DEACTIVATION)
{
/* Not yet in OPERATION state, perform first reset */
if (Llcp->eRole == phFriNfc_LlcpMac_ePeerTypeInitiator)
{
Llcp->state = PHFRINFC_LLCP_STATE_OPERATION_SEND;
}
else
{
Llcp->state = PHFRINFC_LLCP_STATE_OPERATION_RECV;
}
}
/* Calculate timer duration */
/* NOTE: nDuration is in 1/100s, and timer system takes values in 1/1000s */
if (Llcp->state == PHFRINFC_LLCP_STATE_OPERATION_RECV)
{
/* Response must be received before LTO announced by remote peer */
nDuration = Llcp->sRemoteParams.lto * 10;
}
else
{
/* Must answer before the local announced LTO */
/* NOTE: to ensure the answer is completely sent before LTO, the
timer is triggered _before_ LTO expiration */
/* TODO: make sure time scope is enough, and avoid use of magic number */
nDuration = (Llcp->sLocalParams.lto * 10) / 2;
}
LLCP_DEBUG("Starting LLCP timer with duration %d", nDuration);
/* Restart timer */
phOsalNfc_Timer_Start(
Llcp->hSymmTimer,
nDuration,
phFriNfc_Llcp_Timer_CB,
Llcp);
}
static NFCSTATUS phFriNfc_Llcp_HandleLinkPacket( phFriNfc_Llcp_t *Llcp,
phNfc_sData_t *psPacket )
{
NFCSTATUS result;
phFriNfc_Llcp_sPacketHeader_t sHeader;
/* Parse header */
phFriNfc_Llcp_Buffer2Header(psPacket->buffer, 0, &sHeader);
/* Check packet type */
switch (sHeader.ptype)
{
case PHFRINFC_LLCP_PTYPE_SYMM:
{
/* Nothing to do, the LTO is handled upon all packet reception */
result = NFCSTATUS_SUCCESS;
break;
}
case PHFRINFC_LLCP_PTYPE_AGF:
{
/* Handle the aggregated packet */
result = phFriNfc_Llcp_HandleAggregatedPacket(Llcp, psPacket);
if (result != NFCSTATUS_SUCCESS)
{
/* Error: invalid info field, dropping frame */
}
break;
}
case PHFRINFC_LLCP_PTYPE_DISC:
{
/* Handle link disconnection request */
result = phFriNfc_Llcp_InternalDeactivate(Llcp);
break;
}
case PHFRINFC_LLCP_PTYPE_FRMR:
{
/* TODO: what to do upon reception of FRMR on Link SAP ? */
result = NFCSTATUS_SUCCESS;
break;
}
case PHFRINFC_LLCP_PTYPE_PAX:
{
/* Ignore PAX when in Normal Operation */
result = NFCSTATUS_SUCCESS;
break;
}
default:
{
/* Error: invalid ptype field, dropping packet */
break;
}
}
return result;
}
static NFCSTATUS phFriNfc_Llcp_HandleTransportPacket( phFriNfc_Llcp_t *Llcp,
phNfc_sData_t *psPacket )
{
phFriNfc_Llcp_Recv_CB_t pfRecvCB;
void *pContext;
NFCSTATUS result = NFCSTATUS_SUCCESS;
phFriNfc_Llcp_sPacketHeader_t sHeader;
/* Forward to upper layer */
if (Llcp->pfRecvCB != NULL)
{
/* Get callback details */
pfRecvCB = Llcp->pfRecvCB;
pContext = Llcp->pRecvContext;
/* Reset callback details */
Llcp->pfRecvCB = NULL;
Llcp->pRecvContext = NULL;
/* Call the callback */
(pfRecvCB)(pContext, psPacket, NFCSTATUS_SUCCESS);
}
return result;
}
static bool_t phFriNfc_Llcp_HandlePendingSend ( phFriNfc_Llcp_t *Llcp )
{
phFriNfc_Llcp_sPacketHeader_t sHeader;
phNfc_sData_t sInfoBuffer;
phFriNfc_Llcp_sPacketHeader_t *psSendHeader = NULL;
phFriNfc_Llcp_sPacketSequence_t *psSendSequence = NULL;
phNfc_sData_t *psSendInfo = NULL;
NFCSTATUS result;
uint8_t bDeallocate = FALSE;
uint8_t return_value = FALSE;
/* Handle pending disconnection request */
if (Llcp->bDiscPendingFlag == TRUE)
{
/* Last send si acheived, send the pending DISC packet */
sHeader.dsap = PHFRINFC_LLCP_SAP_LINK;
sHeader.ssap = PHFRINFC_LLCP_SAP_LINK;
sHeader.ptype = PHFRINFC_LLCP_PTYPE_DISC;
/* Set send params */
psSendHeader = &sHeader;
/* Reset flag */
Llcp->bDiscPendingFlag = FALSE;
}
/* Handle pending frame reject request */
else if (Llcp->bFrmrPendingFlag == TRUE)
{
/* Last send si acheived, send the pending FRMR packet */
sInfoBuffer.buffer = Llcp->pFrmrInfo;
sInfoBuffer.length = sizeof(Llcp->pFrmrInfo);
/* Set send params */
psSendHeader = &Llcp->sFrmrHeader;
psSendInfo = &sInfoBuffer;
/* Reset flag */
Llcp->bFrmrPendingFlag = FALSE;
}
/* Handle pending service frame */
else if (Llcp->pfSendCB != NULL)
{
/* Set send params */
psSendHeader = Llcp->psSendHeader;
psSendSequence = Llcp->psSendSequence;
psSendInfo = Llcp->psSendInfo;
/* Reset pending send infos */
Llcp->psSendHeader = NULL;
Llcp->psSendSequence = NULL;
Llcp->psSendInfo = NULL;
bDeallocate = TRUE;
}
/* Perform send, if needed */
if (psSendHeader != NULL)
{
result = phFriNfc_Llcp_InternalSend(Llcp, psSendHeader, psSendSequence, psSendInfo);
if ((result != NFCSTATUS_SUCCESS) && (result != NFCSTATUS_PENDING))
{
/* Error: send failed, impossible to recover */
phFriNfc_Llcp_InternalDeactivate(Llcp);
}
return_value = TRUE;
} else if (Llcp->pfSendCB == NULL) {
// Nothing to send, send SYMM instead to allow peer to send something
// if it wants.
phFriNfc_Llcp_SendSymm(Llcp);
return_value = TRUE;
}
clean_and_return:
if (bDeallocate)
{
phFriNfc_Llcp_Deallocate(psSendInfo);
}
return return_value;
}
static NFCSTATUS phFriNfc_Llcp_HandleIncomingPacket( phFriNfc_Llcp_t *Llcp,
phNfc_sData_t *psPacket )
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
phFriNfc_Llcp_sPacketHeader_t sHeader;
/* Parse header */
phFriNfc_Llcp_Buffer2Header(psPacket->buffer, 0, &sHeader);
/* Check destination */
if (sHeader.dsap == PHFRINFC_LLCP_SAP_LINK)
{
/* Handle packet as destinated to the Link SAP */
status = phFriNfc_Llcp_HandleLinkPacket(Llcp, psPacket);
}
else if (sHeader.dsap >= PHFRINFC_LLCP_SAP_NUMBER)
{
/* NOTE: this cannot happen since "psHeader->dsap" is only 6-bit wide */
status = NFCSTATUS_FAILED;
}
else
{
/* Handle packet as destinated to the SDP and transport SAPs */
status = phFriNfc_Llcp_HandleTransportPacket(Llcp, psPacket);
}
return status;
}
static void phFriNfc_Llcp_Receive_CB( void *pContext,
NFCSTATUS status,
phNfc_sData_t *psData)
{
/* Get monitor from context */
phFriNfc_Llcp_t *Llcp = (phFriNfc_Llcp_t*)pContext;
NFCSTATUS result = NFCSTATUS_SUCCESS;
phFriNfc_Llcp_sPacketHeader_t sPacketHeader;
/* Check reception status and for pending disconnection */
if ((status != NFCSTATUS_SUCCESS) || (Llcp->bDiscPendingFlag == TRUE))
{
LLCP_DEBUG("\nReceived LLCP packet error - status = 0x%04x", status);
/* Reset disconnection operation */
Llcp->bDiscPendingFlag = FALSE;
/* Deactivate the link */
phFriNfc_Llcp_InternalDeactivate(Llcp);
return;
}
/* Parse header */
phFriNfc_Llcp_Buffer2Header(psData->buffer, 0, &sPacketHeader);
if (sPacketHeader.ptype != PHFRINFC_LLCP_PTYPE_SYMM)
{
LLCP_PRINT_BUFFER("\nReceived LLCP packet :", psData->buffer, psData->length);
}
else
{
LLCP_PRINT("?");
}
/* Check new link status */
switch(Llcp->state)
{
/* Handle packets in PAX-waiting state */
case PHFRINFC_LLCP_STATE_PAX:
{
/* Check packet type */
if (sPacketHeader.ptype == PHFRINFC_LLCP_PTYPE_PAX)
{
/* Params exchanged during MAC activation, try LLC activation */
result = phFriNfc_Llcp_InternalActivate(Llcp, psData+PHFRINFC_LLCP_PACKET_HEADER_SIZE);
/* If the local LLC is the target, it must answer the PAX */
if (Llcp->eRole == phFriNfc_LlcpMac_ePeerTypeTarget)
{
/* Send PAX */
result = phFriNfc_Llcp_SendPax(Llcp, &Llcp->sLocalParams);
}
}
else
{
/* Warning: Received packet with unhandled type in PAX-waiting state, drop it */
}
break;
}
/* Handle normal operation packets */
case PHFRINFC_LLCP_STATE_OPERATION_RECV:
case PHFRINFC_LLCP_STATE_OPERATION_SEND:
{
/* Handle Symmetry procedure by resetting LTO timer */
phFriNfc_Llcp_ResetLTO(Llcp);
/* Handle packet */
result = phFriNfc_Llcp_HandleIncomingPacket(Llcp, psData);
if ( (result != NFCSTATUS_SUCCESS) &&
(result != NFCSTATUS_PENDING) )
{
/* TODO: Error: invalid frame */
}
/* Perform pending send request, if any */
phFriNfc_Llcp_HandlePendingSend(Llcp);
break;
}
default:
{
/* Warning: Should not receive packets in other states, drop them */
}
}
/* Restart reception */
Llcp->sRxBuffer.length = Llcp->nRxBufferLength;
phFriNfc_LlcpMac_Receive(&Llcp->MAC, &Llcp->sRxBuffer, phFriNfc_Llcp_Receive_CB, Llcp);
}
static void phFriNfc_Llcp_Send_CB( void *pContext,
NFCSTATUS status )
{
/* Get monitor from context */
phFriNfc_Llcp_t *Llcp = (phFriNfc_Llcp_t*)pContext;
phFriNfc_Llcp_Send_CB_t pfSendCB;
void *pSendContext;
/* Call the upper layer callback if last packet sent was */
/* NOTE: if Llcp->psSendHeader is not NULL, this means that the send operation is still not initiated */
if (Llcp->psSendHeader == NULL)
{
if (Llcp->pfSendCB != NULL)
{
/* Get Callback params */
pfSendCB = Llcp->pfSendCB;
pSendContext = Llcp->pSendContext;
/* Reset callback params */
Llcp->pfSendCB = NULL;
Llcp->pSendContext = NULL;
/* Call the callback */
(pfSendCB)(pSendContext, status);
}
}
/* Check reception status */
if (status != NFCSTATUS_SUCCESS)
{
/* Error: Reception failed, link must be down */
phFriNfc_Llcp_InternalDeactivate(Llcp);
}
}
static NFCSTATUS phFriNfc_Llcp_InternalSend( phFriNfc_Llcp_t *Llcp,
phFriNfc_Llcp_sPacketHeader_t *psHeader,
phFriNfc_Llcp_sPacketSequence_t *psSequence,
phNfc_sData_t *psInfo )
{
NFCSTATUS status;
phNfc_sData_t *psRawPacket = &Llcp->sTxBuffer; /* Use internal Tx buffer */
/* Handle Symmetry procedure */
phFriNfc_Llcp_ResetLTO(Llcp);
/* Generate raw packet to send (aggregate header + sequence + info fields) */
psRawPacket->length = 0;
psRawPacket->length += phFriNfc_Llcp_Header2Buffer(psHeader, psRawPacket->buffer, psRawPacket->length);
if (psSequence != NULL)
{
psRawPacket->length += phFriNfc_Llcp_Sequence2Buffer(psSequence, psRawPacket->buffer, psRawPacket->length);
}
if (psInfo != NULL)
{
memcpy(psRawPacket->buffer + psRawPacket->length, psInfo->buffer, psInfo->length);
psRawPacket->length += psInfo->length;
}
if (psHeader->ptype != PHFRINFC_LLCP_PTYPE_SYMM)
{
LLCP_PRINT_BUFFER("\nSending LLCP packet :", psRawPacket->buffer, psRawPacket->length);
}
else
{
LLCP_PRINT("!");
}
/* Send raw packet */
status = phFriNfc_LlcpMac_Send (
&Llcp->MAC,
psRawPacket,
phFriNfc_Llcp_Send_CB,
Llcp );
return status;
}
/* ---------------------------- Public functions ------------------------------- */
NFCSTATUS phFriNfc_Llcp_EncodeLinkParams( phNfc_sData_t *psRawBuffer,
phFriNfc_Llcp_sLinkParameters_t *psLinkParams,
uint8_t nVersion )
{
uint32_t nOffset = 0;
uint16_t miux;
uint16_t wks;
uint8_t pValue[2];
NFCSTATUS result = NFCSTATUS_SUCCESS;
/* Check parameters */
if ((psRawBuffer == NULL) || (psLinkParams == NULL))
{
return NFCSTATUS_INVALID_PARAMETER;
}
/* Encode mandatory VERSION field */
if (result == NFCSTATUS_SUCCESS)
{
result = phFriNfc_Llcp_EncodeTLV(
psRawBuffer,
&nOffset,
PHFRINFC_LLCP_TLV_TYPE_VERSION,
PHFRINFC_LLCP_TLV_LENGTH_VERSION,
&nVersion);
}
/* Encode mandatory VERSION field */
if (result == NFCSTATUS_SUCCESS)
{
/* Encode MIUX field, if needed */
if (psLinkParams->miu != PHFRINFC_LLCP_MIU_DEFAULT)
{
miux = (psLinkParams->miu - PHFRINFC_LLCP_MIU_DEFAULT) & PHFRINFC_LLCP_TLV_MIUX_MASK;
pValue[0] = (miux >> 8) & 0xFF;
pValue[1] = miux & 0xFF;
result = phFriNfc_Llcp_EncodeTLV(
psRawBuffer,
&nOffset,
PHFRINFC_LLCP_TLV_TYPE_MIUX,
PHFRINFC_LLCP_TLV_LENGTH_MIUX,
pValue);
}
}
/* Encode WKS field */
if (result == NFCSTATUS_SUCCESS)
{
wks = psLinkParams->wks | PHFRINFC_LLCP_TLV_WKS_MASK;
pValue[0] = (wks >> 8) & 0xFF;
pValue[1] = wks & 0xFF;
result = phFriNfc_Llcp_EncodeTLV(
psRawBuffer,
&nOffset,
PHFRINFC_LLCP_TLV_TYPE_WKS,
PHFRINFC_LLCP_TLV_LENGTH_WKS,
pValue);
}
/* Encode LTO field, if needed */
if (result == NFCSTATUS_SUCCESS)
{
if (psLinkParams->lto != PHFRINFC_LLCP_LTO_DEFAULT)
{
result = phFriNfc_Llcp_EncodeTLV(
psRawBuffer,
&nOffset,
PHFRINFC_LLCP_TLV_TYPE_LTO,
PHFRINFC_LLCP_TLV_LENGTH_LTO,
&psLinkParams->lto);
}
}
/* Encode OPT field, if needed */
if (result == NFCSTATUS_SUCCESS)
{
if (psLinkParams->option != PHFRINFC_LLCP_OPTION_DEFAULT)
{
result = phFriNfc_Llcp_EncodeTLV(
psRawBuffer,
&nOffset,
PHFRINFC_LLCP_TLV_TYPE_OPT,
PHFRINFC_LLCP_TLV_LENGTH_OPT,
&psLinkParams->option);
}
}
if (result != NFCSTATUS_SUCCESS)
{
/* Error: failed to encode TLV */
return NFCSTATUS_FAILED;
}
/* Save new buffer size */
psRawBuffer->length = nOffset;
return result;
}
NFCSTATUS phFriNfc_Llcp_Reset( phFriNfc_Llcp_t *Llcp,
void *LowerDevice,
phFriNfc_Llcp_sLinkParameters_t *psLinkParams,
void *pRxBuffer,
uint16_t nRxBufferLength,
void *pTxBuffer,
uint16_t nTxBufferLength,
phFriNfc_Llcp_LinkStatus_CB_t pfLink_CB,
void *pContext )
{
const uint16_t nMaxHeaderSize = PHFRINFC_LLCP_PACKET_HEADER_SIZE +
PHFRINFC_LLCP_PACKET_SEQUENCE_SIZE;
NFCSTATUS result;
/* Check parameters presence */
if ((Llcp == NULL) || (LowerDevice == NULL) || (pfLink_CB == NULL) ||
(pRxBuffer == NULL) || (pTxBuffer == NULL) )
{
return NFCSTATUS_INVALID_PARAMETER;
}
/* Check parameters value */
if (psLinkParams->miu < PHFRINFC_LLCP_MIU_DEFAULT)
{
return NFCSTATUS_INVALID_PARAMETER;
}
/* Check if buffers are large enough to support minimal MIU */
if ((nRxBufferLength < (nMaxHeaderSize + PHFRINFC_LLCP_MIU_DEFAULT)) ||
(nTxBufferLength < (nMaxHeaderSize + PHFRINFC_LLCP_MIU_DEFAULT)) )
{
return NFCSTATUS_BUFFER_TOO_SMALL;
}
/* Check compatibility between reception buffer size and announced MIU */
if (nRxBufferLength < (nMaxHeaderSize + psLinkParams->miu))
{
return NFCSTATUS_BUFFER_TOO_SMALL;
}
/* Start with a zero-filled monitor */
memset(Llcp, 0x00, sizeof(phFriNfc_Llcp_t));
/* Reset the MAC Mapping layer */
result = phFriNfc_LlcpMac_Reset(&Llcp->MAC, LowerDevice, phFriNfc_Llcp_LinkStatus_CB, Llcp);
if (result != NFCSTATUS_SUCCESS) {
return result;
}
/* Save the working buffers */
Llcp->sRxBuffer.buffer = pRxBuffer;
Llcp->sRxBuffer.length = nRxBufferLength;
Llcp->nRxBufferLength = nRxBufferLength;
Llcp->sTxBuffer.buffer = pTxBuffer;
Llcp->sTxBuffer.length = nTxBufferLength;
Llcp->nTxBufferLength = nTxBufferLength;
/* Save the link status callback references */
Llcp->pfLink_CB = pfLink_CB;
Llcp->pLinkContext = pContext;
/* Save the local link parameters */
memcpy(&Llcp->sLocalParams, psLinkParams, sizeof(phFriNfc_Llcp_sLinkParameters_t));
return NFCSTATUS_SUCCESS;
}
NFCSTATUS phFriNfc_Llcp_ChkLlcp( phFriNfc_Llcp_t *Llcp,
phHal_sRemoteDevInformation_t *psRemoteDevInfo,
phFriNfc_Llcp_Check_CB_t pfCheck_CB,
void *pContext )
{
/* Check parameters */
if ( (Llcp == NULL) || (psRemoteDevInfo == NULL) || (pfCheck_CB == NULL) )
{
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Check current state */
if( Llcp->state != PHFRINFC_LLCP_STATE_RESET_INIT ) {
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_STATE);
}
/* Save the compliance check callback */
Llcp->pfChk_CB = pfCheck_CB;
Llcp->pChkContext = pContext;
/* Forward check request to MAC layer */
return phFriNfc_LlcpMac_ChkLlcp(&Llcp->MAC, psRemoteDevInfo, phFriNfc_Llcp_ChkLlcp_CB, (void*)Llcp);
}
NFCSTATUS phFriNfc_Llcp_Activate( phFriNfc_Llcp_t *Llcp )
{
/* Check parameters */
if (Llcp == NULL)
{
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Check current state */
if( Llcp->state != PHFRINFC_LLCP_STATE_CHECKED ) {
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_STATE);
}
/* Update state */
Llcp->state = PHFRINFC_LLCP_STATE_ACTIVATION;
/* Forward check request to MAC layer */
return phFriNfc_LlcpMac_Activate(&Llcp->MAC);
}
NFCSTATUS phFriNfc_Llcp_Deactivate( phFriNfc_Llcp_t *Llcp )
{
NFCSTATUS status;
/* Check parameters */
if (Llcp == NULL)
{
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Check current state */
if( (Llcp->state != PHFRINFC_LLCP_STATE_OPERATION_RECV) &&
(Llcp->state != PHFRINFC_LLCP_STATE_OPERATION_SEND) ) {
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_STATE);
}
/* Send DISC packet */
status = phFriNfc_Llcp_SendDisconnect(Llcp);
if (status == NFCSTATUS_PENDING)
{
/* Wait for packet to be sent before deactivate link */
return status;
}
/* Perform actual deactivation */
return phFriNfc_Llcp_InternalDeactivate(Llcp);
}
NFCSTATUS phFriNfc_Llcp_GetLocalInfo( phFriNfc_Llcp_t *Llcp,
phFriNfc_Llcp_sLinkParameters_t *pParams )
{
/* Check parameters */
if ((Llcp == NULL) || (pParams == NULL))
{
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Copy response */
memcpy(pParams, &Llcp->sLocalParams, sizeof(phFriNfc_Llcp_sLinkParameters_t));
return NFCSTATUS_SUCCESS;
}
NFCSTATUS phFriNfc_Llcp_GetRemoteInfo( phFriNfc_Llcp_t *Llcp,
phFriNfc_Llcp_sLinkParameters_t *pParams )
{
/* Check parameters */
if ((Llcp == NULL) || (pParams == NULL))
{
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Copy response */
memcpy(pParams, &Llcp->sRemoteParams, sizeof(phFriNfc_Llcp_sLinkParameters_t));
return NFCSTATUS_SUCCESS;
}
NFCSTATUS phFriNfc_Llcp_Send( phFriNfc_Llcp_t *Llcp,
phFriNfc_Llcp_sPacketHeader_t *psHeader,
phFriNfc_Llcp_sPacketSequence_t *psSequence,
phNfc_sData_t *psInfo,
phFriNfc_Llcp_Send_CB_t pfSend_CB,
void *pContext )
{
NFCSTATUS result;
/* Check parameters */
if ((Llcp == NULL) || (psHeader == NULL) || (pfSend_CB == NULL))
{
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Check if previous phFriNfc_Llcp_Send() has finished */
if (Llcp->pfSendCB != NULL)
{
/* Error: a send operation is already running */
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_REJECTED);
}
/* Save the callback parameters */
Llcp->pfSendCB = pfSend_CB;
Llcp->pSendContext = pContext;
if (Llcp->state == PHFRINFC_LLCP_STATE_OPERATION_SEND)
{
/* Ready to send */
result = phFriNfc_Llcp_InternalSend(Llcp, psHeader, psSequence, psInfo);
}
else if (Llcp->state == PHFRINFC_LLCP_STATE_OPERATION_RECV)
{
/* Not ready to send, save send params for later use */
Llcp->psSendHeader = psHeader;
Llcp->psSendSequence = psSequence;
Llcp->psSendInfo = phFriNfc_Llcp_AllocateAndCopy(psInfo);
result = NFCSTATUS_PENDING;
}
else
{
/* Incorrect state for sending ! */
result = PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_STATE);;
}
if (result != NFCSTATUS_PENDING) {
Llcp->pfSendCB = NULL;
}
return result;
}
NFCSTATUS phFriNfc_Llcp_Recv( phFriNfc_Llcp_t *Llcp,
phFriNfc_Llcp_Recv_CB_t pfRecv_CB,
void *pContext )
{
NFCSTATUS result = NFCSTATUS_SUCCESS;
/* Check parameters */
if ((Llcp == NULL) || (pfRecv_CB == NULL))
{
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER);
}
/* Check if previous phFriNfc_Llcp_Recv() has finished */
if (Llcp->pfRecvCB != NULL)
{
/* Error: a send operation is already running */
return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_REJECTED);
}
/* Save the callback parameters */
Llcp->pfRecvCB = pfRecv_CB;
Llcp->pRecvContext = pContext;
/* NOTE: nothing more to do, the receive function is called in background */
return result;
}