| /* |
| * 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_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_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 |
| { |
| /* 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; |
| } |