diff options
author | Nick Pelly <npelly@google.com> | 2010-09-23 12:47:58 -0700 |
---|---|---|
committer | Nick Pelly <npelly@google.com> | 2010-09-23 13:53:18 -0700 |
commit | 5d9927ba30ba449badb9f6df0fbeb4d6aedc6e2a (patch) | |
tree | 190f9251c6db03d3550ec7f30b51a2561c01d9cf /src/phFriNfc_Llcp.c | |
parent | 4ff7c86a2c706b150078274455406f1b04966e1a (diff) | |
download | external_libnfc-nxp-5d9927ba30ba449badb9f6df0fbeb4d6aedc6e2a.zip external_libnfc-nxp-5d9927ba30ba449badb9f6df0fbeb4d6aedc6e2a.tar.gz external_libnfc-nxp-5d9927ba30ba449badb9f6df0fbeb4d6aedc6e2a.tar.bz2 |
Initial libnfc checkin
Source: Trusted_NFC_Device_Host_AA03.01e02_google.zip code drop (23-Sep-2010)
Change-Id: Ie47f18423f949a8d3e0815d13f55c814312add24
Signed-off-by: Nick Pelly <npelly@google.com>
Diffstat (limited to 'src/phFriNfc_Llcp.c')
-rw-r--r-- | src/phFriNfc_Llcp.c | 1381 |
1 files changed, 1381 insertions, 0 deletions
diff --git a/src/phFriNfc_Llcp.c b/src/phFriNfc_Llcp.c new file mode 100644 index 0000000..b70ba03 --- /dev/null +++ b/src/phFriNfc_Llcp.c @@ -0,0 +1,1381 @@ +/* + * 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 NFCSTATUS phFriNfc_Llcp_InternalDeactivate( phFriNfc_Llcp_t *Llcp ) +{ + 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); + + /* 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; + bool_t bPendingFlag; + + /* Check for pending messages to send */ + bPendingFlag = phFriNfc_Llcp_HandlePendingSend(Llcp); + + if (bPendingFlag == FALSE) + { + /* No send pending, send a SYMM instead */ + 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); + } + else + { + /* A pending send has been sent, there is no need to send SYMM */ + return NFCSTATUS_SUCCESS; + } +} + + +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; + 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 += sizeof(sInfo.length); + sInfo.length -= sizeof(sInfo.length); + /* 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 += sizeof(sInfo.length); + sInfo.length -= sizeof(sInfo.length); + /* Handle aggregated packet */ + status = phFriNfc_Llcp_HandleIncomingPacket(Llcp, &sInfo); + 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 */ + return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER); + } + /* 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 */ + return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER); + } + /* 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 */ + return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER); + } + /* 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 */ + return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER); + } + /* 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 */ + return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER); + } + /* Get OPT */ + sParams.option = sValueBuffer.buffer[0] & PHFRINFC_LLCP_TLV_OPT_MASK; + break; + } + default: + { + /* Error : Unknown Type */ + return PHNFCSTVAL(CID_FRI_NFC_LLCP, NFCSTATUS_INVALID_PARAMETER); + } + } + } + + /* 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_CHECKED; + + 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_eType_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 + { + /* 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; + } + + /* 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; + + /* 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; + } + + /* 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 TRUE; + } + + /* Nothing to do */ + return FALSE; +} + +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; + + /* 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 reception status and for pending disconnection */ + if ((status != NFCSTATUS_SUCCESS) || (Llcp->bDiscPendingFlag == TRUE)) + { + /* Reset disconnection operation */ + Llcp->bDiscPendingFlag = FALSE; + /* Deactivate the link */ + phFriNfc_Llcp_InternalDeactivate(Llcp); + return; + } + + /* 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; + + /* Check reception status */ + if (status != NFCSTATUS_SUCCESS) + { + /* Error: Reception failed, link must be down */ + phFriNfc_Llcp_InternalDeactivate(Llcp); + return; + } + + /* 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); + } + } +} + + +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) + { + /* Not ready to send, save send params for later use */ + Llcp->psSendHeader = psHeader; + Llcp->psSendSequence = psSequence; + Llcp->psSendInfo = psInfo; + result = NFCSTATUS_PENDING; + } + else + { + /* No send pending, send immediately */ + result = phFriNfc_Llcp_InternalSend(Llcp, psHeader, psSequence, psInfo); + } + 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; +} |