summaryrefslogtreecommitdiffstats
path: root/src/phFriNfc_Llcp.c
diff options
context:
space:
mode:
authorNick Pelly <npelly@google.com>2010-09-23 12:47:58 -0700
committerNick Pelly <npelly@google.com>2010-09-23 13:53:18 -0700
commit5d9927ba30ba449badb9f6df0fbeb4d6aedc6e2a (patch)
tree190f9251c6db03d3550ec7f30b51a2561c01d9cf /src/phFriNfc_Llcp.c
parent4ff7c86a2c706b150078274455406f1b04966e1a (diff)
downloadexternal_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.c1381
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;
+}