/*
* 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_LlcpTransport.c
* \brief
*
* Project: NFC-FRI
*
*/
/*include files*/
#include <cutils/log.h>
#include <phOsalNfc.h>
#include <phLibNfcStatus.h>
#include <phLibNfc.h>
#include <phNfcLlcpTypes.h>
#include <phFriNfc_Llcp.h>
#include <phFriNfc_LlcpTransport.h>
#include <phFriNfc_LlcpTransport_Connectionless.h>
#include <phFriNfc_LlcpTransport_Connection.h>
/* local macros */
/* Check if (a <= x < b) */
#define IS_BETWEEN(x, a, b) (((x)>=(a)) && ((x)<(b)))
static NFCSTATUS phFriNfc_LlcpTransport_RegisterName(phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
uint8_t nSap,
phNfc_sData_t *psServiceName);
static NFCSTATUS phFriNfc_LlcpTransport_DiscoverServicesEx(phFriNfc_LlcpTransport_t *psTransport);
static void phFriNfc_LlcpTransport_Send_CB(void *pContext,
NFCSTATUS status);
static NFCSTATUS phFriNfc_LlcpTransport_GetFreeSap(phFriNfc_LlcpTransport_t * psTransport, phNfc_sData_t *psServiceName, uint8_t * pnSap)
{
uint8_t i;
uint8_t sap;
uint8_t min_sap_range, max_sap_range;
phFriNfc_LlcpTransport_Socket_t* pSocketTable = psTransport->pSocketTable;
/* Calculate authorized SAP range */
if ((psServiceName != NULL) && (psServiceName->length > 0))
{
/* Make sure that we will return the same SAP if service name was already used in the past */
for(i=0 ; i<PHFRINFC_LLCP_SDP_ADVERTISED_NB ; i++)
{
if((psTransport->pCachedServiceNames[i].sServiceName.length > 0) &&
(memcmp(psTransport->pCachedServiceNames[i].sServiceName.buffer, psServiceName->buffer, psServiceName->length) == 0))
{
/* Service name matched in cached service names list */
*pnSap = psTransport->pCachedServiceNames[i].nSap;
return NFCSTATUS_SUCCESS;
}
}
/* SDP advertised service */
min_sap_range = PHFRINFC_LLCP_SAP_SDP_ADVERTISED_FIRST;
max_sap_range = PHFRINFC_LLCP_SAP_SDP_UNADVERTISED_FIRST;
}
else
{
/* Non-SDP advertised service */
min_sap_range = PHFRINFC_LLCP_SAP_SDP_UNADVERTISED_FIRST;
max_sap_range = PHFRINFC_LLCP_SAP_NUMBER;
}
/* Try all possible SAPs */
for(sap=min_sap_range ; sap<max_sap_range ; sap++)
{
/* Go through socket list to check if current SAP is in use */
for(i=0 ; i<PHFRINFC_LLCP_NB_SOCKET_MAX ; i++)
{
if((pSocketTable[i].eSocket_State >= phFriNfc_LlcpTransportSocket_eSocketBound) &&
(pSocketTable[i].socket_sSap == sap))
{
/* SAP is already in use */
break;
}
}
if (i >= PHFRINFC_LLCP_NB_SOCKET_MAX)
{
/* No socket is using current SAP, proceed with binding */
*pnSap = sap;
return NFCSTATUS_SUCCESS;
}
}
/* If we reach this point, it means that no SAP is free */
return NFCSTATUS_INSUFFICIENT_RESOURCES;
}
static NFCSTATUS phFriNfc_LlcpTransport_EncodeSdreqTlv(phNfc_sData_t *psTlvData,
uint32_t *pOffset,
uint8_t nTid,
phNfc_sData_t *psServiceName)
{
NFCSTATUS result;
uint32_t nTlvOffset = *pOffset;
uint32_t nTlvStartOffset = nTlvOffset;
/* Encode the TID */
result = phFriNfc_Llcp_EncodeTLV(psTlvData,
&nTlvOffset,
PHFRINFC_LLCP_TLV_TYPE_SDREQ,
1,
&nTid);
if (result != NFCSTATUS_SUCCESS)
{
goto clean_and_return;
}
/* Encode the service name itself */
result = phFriNfc_Llcp_AppendTLV(psTlvData,
nTlvStartOffset,
&nTlvOffset,
psServiceName->length,
psServiceName->buffer);
if (result != NFCSTATUS_SUCCESS)
{
goto clean_and_return;
}
clean_and_return:
/* Save offset if no error occured */
if (result == NFCSTATUS_SUCCESS)
{
*pOffset = nTlvOffset;
}
return result;
}
static NFCSTATUS phFriNfc_LlcpTransport_EncodeSdresTlv(phNfc_sData_t *psTlvData,
uint32_t *pOffset,
uint8_t nTid,
uint8_t nSap)
{
NFCSTATUS result;
uint32_t nTlvStartOffset = *pOffset;
/* Encode the TID */
result = phFriNfc_Llcp_EncodeTLV(psTlvData,
pOffset,
PHFRINFC_LLCP_TLV_TYPE_SDRES,
1,
&nTid);
if (result != NFCSTATUS_SUCCESS)
{
goto clean_and_return;
}
/* Encode the service name itself */
result = phFriNfc_Llcp_AppendTLV(psTlvData,
nTlvStartOffset,
pOffset,
1,
&nSap);
if (result != NFCSTATUS_SUCCESS)
{
goto clean_and_return;
}
clean_and_return:
/* Restore previous offset if an error occured */
if (result != NFCSTATUS_SUCCESS)
{
*pOffset = nTlvStartOffset;
}
return result;
}
static phFriNfc_LlcpTransport_Socket_t* phFriNfc_LlcpTransport_ServiceNameLoockup(phFriNfc_LlcpTransport_t *psTransport,
phNfc_sData_t *pServiceName)
{
uint32_t index;
uint8_t cacheIndex;
phFriNfc_Llcp_CachedServiceName_t * pCachedServiceName;
phFriNfc_LlcpTransport_Socket_t * pSocket;
/* Search a socket with the SN */
for(index=0;index<PHFRINFC_LLCP_NB_SOCKET_MAX;index++)
{
pSocket = &psTransport->pSocketTable[index];
/* Test if the CO socket is in Listen state or the CL socket is bound
and if its SN is the good one */
if((((pSocket->eSocket_Type == phFriNfc_LlcpTransport_eConnectionOriented)
&& (pSocket->eSocket_State == phFriNfc_LlcpTransportSocket_eSocketRegistered))
|| ((pSocket->eSocket_Type == phFriNfc_LlcpTransport_eConnectionLess)
&& (pSocket->eSocket_State == phFriNfc_LlcpTransportSocket_eSocketBound)))
&&
(pServiceName->length == pSocket->sServiceName.length)
&& !memcmp(pServiceName->buffer, pSocket->sServiceName.buffer, pServiceName->length))
{
/* Add new entry to cached service name/sap if not already in table */
for(cacheIndex=0;cacheIndex<PHFRINFC_LLCP_SDP_ADVERTISED_NB;cacheIndex++)
{
pCachedServiceName = &psTransport->pCachedServiceNames[cacheIndex];
if (pCachedServiceName->sServiceName.buffer != NULL)
{
if ((pCachedServiceName->sServiceName.length == pServiceName->length) &&
(memcmp(pCachedServiceName->sServiceName.buffer, pServiceName->buffer, pServiceName->length) == 0))
{
/* Already registered */
break;
}
}
else
{
/* Reached end of existing entries and not found the service name,
* => Add the new entry
*/
pCachedServiceName->nSap = pSocket->socket_sSap;
pCachedServiceName->sServiceName.buffer = phOsalNfc_GetMemory(pServiceName->length);
if (pCachedServiceName->sServiceName.buffer == NULL)
{
/* Unable to cache this entry, so report this service as not found */
return NULL;
}
memcpy(pCachedServiceName->sServiceName.buffer, pServiceName->buffer, pServiceName->length);
pCachedServiceName->sServiceName.length = pServiceName->length;
break;
}
}
return pSocket;
}
}
return NULL;
}
static NFCSTATUS phFriNfc_LlcpTransport_DiscoveryAnswer(phFriNfc_LlcpTransport_t *psTransport)
{
NFCSTATUS result = NFCSTATUS_PENDING;
phNfc_sData_t sInfoBuffer;
uint32_t nTlvOffset;
uint8_t index;
uint8_t nTid, nSap;
/* Test if a send is pending */
if(!testAndSetSendPending(psTransport))
{
/* Set the header */
psTransport->sLlcpHeader.dsap = PHFRINFC_LLCP_SAP_SDP;
psTransport->sLlcpHeader.ptype = PHFRINFC_LLCP_PTYPE_SNL;
psTransport->sLlcpHeader.ssap = PHFRINFC_LLCP_SAP_SDP;
/* Prepare the info buffer */
sInfoBuffer.buffer = psTransport->pDiscoveryBuffer;
sInfoBuffer.length = sizeof(psTransport->pDiscoveryBuffer);
/* Encode as many requests as possible */
nTlvOffset = 0;
for(index=0 ; index<psTransport->nDiscoveryResListSize ; index++)
{
/* Get current TID/SAP and try to encode them in SNL frame */
nTid = psTransport->nDiscoveryResTidList[index];
nSap = psTransport->nDiscoveryResSapList[index];
/* Encode response */
result = phFriNfc_LlcpTransport_EncodeSdresTlv(&sInfoBuffer,
&nTlvOffset,
nTid,
nSap);
if (result != NFCSTATUS_SUCCESS)
{
/* Impossible to fit the entire response */
/* TODO: support reponse framgentation */
break;
}
}
/* Reset list size to be able to handle a new request */
psTransport->nDiscoveryResListSize = 0;
/* Update buffer length to match real TLV size */
sInfoBuffer.length = nTlvOffset;
/* Send SNL frame */
result = phFriNfc_Llcp_Send(psTransport->pLlcp,
&psTransport->sLlcpHeader,
NULL,
&sInfoBuffer,
phFriNfc_LlcpTransport_Send_CB,
psTransport);
}
else
{
/* Impossible to send now, this function will be called again on next opportunity */
}
return result;
}
static void Handle_Discovery_IncomingFrame(phFriNfc_LlcpTransport_t *psTransport,
phNfc_sData_t *psData)
{
NFCSTATUS result;
phNfc_sData_t sValue;
phNfc_sData_t sResponseData;
phNfc_sData_t sServiceName;
uint32_t nInTlvOffset;
uint8_t nType;
uint8_t nTid;
uint8_t nSap;
pphFriNfc_Cr_t pfSavedCb;
void *pfSavedContext;
phFriNfc_LlcpTransport_Socket_t *pSocket;
/* Prepare buffer */
sResponseData.buffer = psTransport->pDiscoveryBuffer;
sResponseData.length = sizeof(psTransport->pDiscoveryBuffer);
/* Parse all TLVs in frame */
nInTlvOffset = 0;
while(nInTlvOffset < psData->length)
{
result = phFriNfc_Llcp_DecodeTLV(psData,
&nInTlvOffset,
&nType,
&sValue );
switch(nType)
{
case PHFRINFC_LLCP_TLV_TYPE_SDREQ:
if (sValue.length < 2)
{
/* Erroneous request, ignore */
break;
}
/* Decode TID */
nTid = sValue.buffer[0];
/* Decode service name */
sServiceName.buffer = sValue.buffer + 1;
sServiceName.length = sValue.length - 1;
/* Handle SDP service name */
if((sServiceName.length == sizeof(PHFRINFC_LLCP_SERVICENAME_SDP)-1)
&& !memcmp(sServiceName.buffer, PHFRINFC_LLCP_SERVICENAME_SDP, sServiceName.length))
{
nSap = PHFRINFC_LLCP_SAP_SDP;
}
else
{
/* Match service name in socket list */
pSocket = phFriNfc_LlcpTransport_ServiceNameLoockup(psTransport, &sServiceName);
if (pSocket != NULL)
{
nSap = pSocket->socket_sSap;
}
else
{
nSap = 0;
}
}
/* Encode response */
if (psTransport->nDiscoveryResListSize < PHFRINFC_LLCP_SNL_RESPONSE_MAX)
{
psTransport->nDiscoveryResSapList[psTransport->nDiscoveryResListSize] = nSap;
psTransport->nDiscoveryResTidList[psTransport->nDiscoveryResListSize] = nTid;
psTransport->nDiscoveryResListSize++;
}
else
{
/* Remote peer is sending more than max. allowed requests (max. 256
different TID values), drop invalid requests to avoid buffer overflow
*/
}
break;
case PHFRINFC_LLCP_TLV_TYPE_SDRES:
if (psTransport->pfDiscover_Cb == NULL)
{
/* Ignore response when no requests are pending */
break;
}
if (sValue.length != 2)
{
/* Erroneous response, ignore it */
break;
}
/* Decode TID and SAP */
nTid = sValue.buffer[0];
if (nTid >= psTransport->nDiscoveryListSize)
{
/* Unkown TID, ignore it */
break;
}
nSap = sValue.buffer[1];
/* Save response */
psTransport->pnDiscoverySapList[nTid] = nSap;
/* Update response counter */
psTransport->nDiscoveryResOffset++;
break;
default:
/* Ignored */
break;
}
}
/* If discovery requests have been received, send response */
if (psTransport->nDiscoveryResListSize > 0)
{
phFriNfc_LlcpTransport_DiscoveryAnswer(psTransport);
}
/* If all discovery responses have been received, trigger callback (if any) */
if ((psTransport->pfDiscover_Cb != NULL) &&
(psTransport->nDiscoveryResOffset >= psTransport->nDiscoveryListSize))
{
pfSavedCb = psTransport->pfDiscover_Cb;
pfSavedContext = psTransport->pDiscoverContext;
psTransport->pfDiscover_Cb = NULL;
psTransport->pDiscoverContext = NULL;
pfSavedCb(pfSavedContext, NFCSTATUS_SUCCESS);
}
}
/* TODO: comment function Transport recv CB */
static void phFriNfc_LlcpTransport__Recv_CB(void *pContext,
phNfc_sData_t *psData,
NFCSTATUS status)
{
phFriNfc_Llcp_sPacketHeader_t sLlcpLocalHeader;
uint8_t dsap;
uint8_t ptype;
uint8_t ssap;
phFriNfc_LlcpTransport_t* pLlcpTransport = (phFriNfc_LlcpTransport_t*)pContext;
if(status != NFCSTATUS_SUCCESS)
{
pLlcpTransport->LinkStatusError = TRUE;
}
else
{
phFriNfc_Llcp_Buffer2Header( psData->buffer,0x00, &sLlcpLocalHeader);
dsap = (uint8_t)sLlcpLocalHeader.dsap;
ptype = (uint8_t)sLlcpLocalHeader.ptype;
ssap = (uint8_t)sLlcpLocalHeader.ssap;
/* Update the length value (without the header length) */
psData->length = psData->length - PHFRINFC_LLCP_PACKET_HEADER_SIZE;
/* Update the buffer pointer */
psData->buffer = psData->buffer + PHFRINFC_LLCP_PACKET_HEADER_SIZE;
switch(ptype)
{
/* Connectionless */
case PHFRINFC_LLCP_PTYPE_UI:
{
Handle_Connectionless_IncommingFrame(pLlcpTransport,
psData,
dsap,
ssap);
}break;
/* Service Discovery Protocol */
case PHFRINFC_LLCP_PTYPE_SNL:
{
if ((ssap == PHFRINFC_LLCP_SAP_SDP) && (dsap == PHFRINFC_LLCP_SAP_SDP))
{
Handle_Discovery_IncomingFrame(pLlcpTransport,
psData);
}
else
{
/* Ignore frame if source and destination are not the SDP service */
}
}break;
/* Connection oriented */
/* NOTE: forward reserved PTYPE to enable FRMR sending */
case PHFRINFC_LLCP_PTYPE_CONNECT:
case PHFRINFC_LLCP_PTYPE_CC:
case PHFRINFC_LLCP_PTYPE_DISC:
case PHFRINFC_LLCP_PTYPE_DM:
case PHFRINFC_LLCP_PTYPE_I:
case PHFRINFC_LLCP_PTYPE_RR:
case PHFRINFC_LLCP_PTYPE_RNR:
case PHFRINFC_LLCP_PTYPE_FRMR:
case PHFRINFC_LLCP_PTYPE_RESERVED1:
case PHFRINFC_LLCP_PTYPE_RESERVED2:
case PHFRINFC_LLCP_PTYPE_RESERVED3:
{
Handle_ConnectionOriented_IncommingFrame(pLlcpTransport,
psData,
dsap,
ptype,
ssap);
}break;
default:
{
}break;
}
/*Restart the Receive Loop */
status = phFriNfc_Llcp_Recv(pLlcpTransport->pLlcp,
phFriNfc_LlcpTransport__Recv_CB,
pLlcpTransport);
}
}
bool_t testAndSetSendPending(phFriNfc_LlcpTransport_t* transport) {
bool_t currentValue;
pthread_mutex_lock(&transport->mutex);
currentValue = transport->bSendPending;
transport->bSendPending = TRUE;
pthread_mutex_unlock(&transport->mutex);
return currentValue;
}
void clearSendPending(phFriNfc_LlcpTransport_t* transport) {
pthread_mutex_lock(&transport->mutex);
transport->bSendPending = FALSE;
pthread_mutex_unlock(&transport->mutex);
}
/* TODO: comment function Transport recv CB */
static void phFriNfc_LlcpTransport_Send_CB(void *pContext,
NFCSTATUS status)
{
phFriNfc_LlcpTransport_t *psTransport = (phFriNfc_LlcpTransport_t*)pContext;
NFCSTATUS result = NFCSTATUS_FAILED;
phNfc_sData_t sFrmrBuffer;
phFriNfc_Llcp_LinkSend_CB_t pfSavedCb;
void *pSavedContext;
phFriNfc_LlcpTransport_Socket_t *pCurrentSocket = NULL;
uint8_t index;
// Store callbacks and socket index, so they can safely be
// overwritten by any code in the callback itself.
pfSavedCb = psTransport->pfLinkSendCb;
pSavedContext = psTransport->pLinkSendContext;
psTransport->pfLinkSendCb = NULL;
psTransport->pLinkSendContext = NULL;
index = psTransport->socketIndex;
/* 1 - Reset the FLAG send pending*/
clearSendPending(psTransport);
/* 2 - Handle pending error responses */
if(psTransport->bFrmrPending)
{
if (!testAndSetSendPending(psTransport)) {
/* Reset FRMR pending */
psTransport->bFrmrPending = FALSE;
/* Send Frmr */
sFrmrBuffer.buffer = psTransport->FrmrInfoBuffer;
sFrmrBuffer.length = 0x04; /* Size of FRMR Information field */
result = phFriNfc_Llcp_Send(psTransport->pLlcp,
&psTransport->sLlcpHeader,
NULL,
&sFrmrBuffer,
phFriNfc_LlcpTransport_Send_CB,
psTransport);
}
}
else if(psTransport->bDmPending)
{
/* Reset DM pending */
psTransport->bDmPending = FALSE;
/* Send DM pending */
result = phFriNfc_LlcpTransport_SendDisconnectMode(psTransport,
psTransport->DmInfoBuffer[0],
psTransport->DmInfoBuffer[1],
psTransport->DmInfoBuffer[2]);
}
/* 3 - Call the original callback */
if (pfSavedCb != NULL)
{
(*pfSavedCb)(pSavedContext, index, status);
}
/* 4 - Handle pending send operations */
/* Check for pending discovery requests/responses */
if (psTransport->nDiscoveryResListSize > 0)
{
phFriNfc_LlcpTransport_DiscoveryAnswer(psTransport);
}
if ( (psTransport->pfDiscover_Cb != NULL) &&
(psTransport->nDiscoveryReqOffset < psTransport->nDiscoveryListSize) )
{
result = phFriNfc_LlcpTransport_DiscoverServicesEx(psTransport);
}
/* Init index */
index = psTransport->socketIndex;
/* Check all sockets for pending operation */
do
{
/* Modulo-increment index */
index = (index + 1) % PHFRINFC_LLCP_NB_SOCKET_MAX;
pCurrentSocket = &psTransport->pSocketTable[index];
/* Dispatch to the corresponding transport layer */
if (pCurrentSocket->eSocket_Type == phFriNfc_LlcpTransport_eConnectionOriented)
{
result = phFriNfc_LlcpTransport_ConnectionOriented_HandlePendingOperations(pCurrentSocket);
}
else if (pCurrentSocket->eSocket_Type == phFriNfc_LlcpTransport_eConnectionLess)
{
result = phFriNfc_LlcpTransport_Connectionless_HandlePendingOperations(pCurrentSocket);
}
if (result != NFCSTATUS_FAILED)
{
/* Stop looping if pending operation has been found */
break;
}
} while(index != psTransport->socketIndex);
}
/* TODO: comment function Transport reset */
NFCSTATUS phFriNfc_LlcpTransport_Reset (phFriNfc_LlcpTransport_t *pLlcpTransport,
phFriNfc_Llcp_t *pLlcp)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
uint8_t i;
/* Check for NULL pointers */
if(pLlcpTransport == NULL || pLlcp == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else
{
/* Reset Transport structure */
pLlcpTransport->pLlcp = pLlcp;
pLlcpTransport->LinkStatusError = FALSE;
pLlcpTransport->bSendPending = FALSE;
pLlcpTransport->bRecvPending = FALSE;
pLlcpTransport->bDmPending = FALSE;
pLlcpTransport->bFrmrPending = FALSE;
pLlcpTransport->socketIndex = FALSE;
pLlcpTransport->LinkStatusError = 0;
pLlcpTransport->pfDiscover_Cb = NULL;
/* Initialize cached service name/sap table */
memset(pLlcpTransport->pCachedServiceNames, 0x00, sizeof(phFriNfc_Llcp_CachedServiceName_t)*PHFRINFC_LLCP_SDP_ADVERTISED_NB);
/* Reset all the socket info in the table */
for(i=0;i<PHFRINFC_LLCP_NB_SOCKET_MAX;i++)
{
pLlcpTransport->pSocketTable[i].eSocket_State = phFriNfc_LlcpTransportSocket_eSocketDefault;
pLlcpTransport->pSocketTable[i].eSocket_Type = phFriNfc_LlcpTransport_eDefaultType;
pLlcpTransport->pSocketTable[i].index = i;
pLlcpTransport->pSocketTable[i].pContext = NULL;
pLlcpTransport->pSocketTable[i].pListenContext = NULL;
pLlcpTransport->pSocketTable[i].pAcceptContext = NULL;
pLlcpTransport->pSocketTable[i].pRejectContext = NULL;
pLlcpTransport->pSocketTable[i].pConnectContext = NULL;
pLlcpTransport->pSocketTable[i].pDisconnectContext = NULL;
pLlcpTransport->pSocketTable[i].pSendContext = NULL;
pLlcpTransport->pSocketTable[i].pRecvContext = NULL;
pLlcpTransport->pSocketTable[i].pSocketErrCb = NULL;
pLlcpTransport->pSocketTable[i].bufferLinearLength = 0;
pLlcpTransport->pSocketTable[i].bufferSendMaxLength = 0;
pLlcpTransport->pSocketTable[i].bufferRwMaxLength = 0;
pLlcpTransport->pSocketTable[i].ReceiverBusyCondition = FALSE;
pLlcpTransport->pSocketTable[i].RemoteBusyConditionInfo = FALSE;
pLlcpTransport->pSocketTable[i].socket_sSap = PHFRINFC_LLCP_SAP_DEFAULT;
pLlcpTransport->pSocketTable[i].socket_dSap = PHFRINFC_LLCP_SAP_DEFAULT;
pLlcpTransport->pSocketTable[i].bSocketRecvPending = FALSE;
pLlcpTransport->pSocketTable[i].bSocketSendPending = FALSE;
pLlcpTransport->pSocketTable[i].bSocketListenPending = FALSE;
pLlcpTransport->pSocketTable[i].bSocketDiscPending = FALSE;
pLlcpTransport->pSocketTable[i].bSocketConnectPending = FALSE;
pLlcpTransport->pSocketTable[i].bSocketAcceptPending = FALSE;
pLlcpTransport->pSocketTable[i].bSocketRRPending = FALSE;
pLlcpTransport->pSocketTable[i].bSocketRNRPending = FALSE;
pLlcpTransport->pSocketTable[i].psTransport = pLlcpTransport;
pLlcpTransport->pSocketTable[i].pfSocketSend_Cb = NULL;
pLlcpTransport->pSocketTable[i].pfSocketRecv_Cb = NULL;
pLlcpTransport->pSocketTable[i].pfSocketRecvFrom_Cb = NULL;
pLlcpTransport->pSocketTable[i].pfSocketListen_Cb = NULL;
pLlcpTransport->pSocketTable[i].pfSocketConnect_Cb = NULL;
pLlcpTransport->pSocketTable[i].pfSocketDisconnect_Cb = NULL;
pLlcpTransport->pSocketTable[i].socket_VS = 0;
pLlcpTransport->pSocketTable[i].socket_VSA = 0;
pLlcpTransport->pSocketTable[i].socket_VR = 0;
pLlcpTransport->pSocketTable[i].socket_VRA = 0;
pLlcpTransport->pSocketTable[i].remoteRW = 0;
pLlcpTransport->pSocketTable[i].localRW = 0;
pLlcpTransport->pSocketTable[i].remoteMIU = 0;
pLlcpTransport->pSocketTable[i].localMIUX = 0;
pLlcpTransport->pSocketTable[i].index = 0;
pLlcpTransport->pSocketTable[i].indexRwRead = 0;
pLlcpTransport->pSocketTable[i].indexRwWrite = 0;
memset(&pLlcpTransport->pSocketTable[i].sSocketOption, 0x00, sizeof(phFriNfc_LlcpTransport_sSocketOptions_t));
if (pLlcpTransport->pSocketTable[i].sServiceName.buffer != NULL) {
phOsalNfc_FreeMemory(pLlcpTransport->pSocketTable[i].sServiceName.buffer);
}
pLlcpTransport->pSocketTable[i].sServiceName.buffer = NULL;
pLlcpTransport->pSocketTable[i].sServiceName.length = 0;
}
/* Start The Receive Loop */
status = phFriNfc_Llcp_Recv(pLlcpTransport->pLlcp,
phFriNfc_LlcpTransport__Recv_CB,
pLlcpTransport);
}
return status;
}
/* TODO: comment function Transport CloseAll */
NFCSTATUS phFriNfc_LlcpTransport_CloseAll (phFriNfc_LlcpTransport_t *pLlcpTransport)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
phFriNfc_Llcp_CachedServiceName_t * pCachedServiceName;
uint8_t i;
/* Check for NULL pointers */
if(pLlcpTransport == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Close all sockets */
for(i=0;i<PHFRINFC_LLCP_NB_SOCKET_MAX;i++)
{
if(pLlcpTransport->pSocketTable[i].eSocket_Type == phFriNfc_LlcpTransport_eConnectionOriented)
{
switch(pLlcpTransport->pSocketTable[i].eSocket_State)
{
case phFriNfc_LlcpTransportSocket_eSocketConnected:
case phFriNfc_LlcpTransportSocket_eSocketConnecting:
case phFriNfc_LlcpTransportSocket_eSocketAccepted:
case phFriNfc_LlcpTransportSocket_eSocketDisconnected:
case phFriNfc_LlcpTransportSocket_eSocketDisconnecting:
case phFriNfc_LlcpTransportSocket_eSocketRejected:
phFriNfc_LlcpTransport_Close(&pLlcpTransport->pSocketTable[i]);
break;
default:
/* Do nothing */
break;
}
}
else
{
phFriNfc_LlcpTransport_Close(&pLlcpTransport->pSocketTable[i]);
}
}
/* Reset cached service name/sap table */
for(i=0;i<PHFRINFC_LLCP_SDP_ADVERTISED_NB;i++)
{
pCachedServiceName = &pLlcpTransport->pCachedServiceNames[i];
pCachedServiceName->nSap = 0;
if (pCachedServiceName->sServiceName.buffer != NULL)
{
phOsalNfc_FreeMemory(pCachedServiceName->sServiceName.buffer);
pCachedServiceName->sServiceName.buffer = NULL;
}
pCachedServiceName->sServiceName.length = 0;
}
return status;
}
/* TODO: comment function Transport LinkSend */
NFCSTATUS phFriNfc_LlcpTransport_LinkSend( phFriNfc_LlcpTransport_t *LlcpTransport,
phFriNfc_Llcp_sPacketHeader_t *psHeader,
phFriNfc_Llcp_sPacketSequence_t *psSequence,
phNfc_sData_t *psInfo,
phFriNfc_Llcp_LinkSend_CB_t pfSend_CB,
uint8_t socketIndex,
void *pContext )
{
NFCSTATUS status;
/* Check if a send is already ongoing */
if (LlcpTransport->pfLinkSendCb != NULL)
{
return NFCSTATUS_BUSY;
}
/* Save callback details */
LlcpTransport->pfLinkSendCb = pfSend_CB;
LlcpTransport->pLinkSendContext = pContext;
LlcpTransport->socketIndex = socketIndex;
/* Call the link-level send function */
status = phFriNfc_Llcp_Send(LlcpTransport->pLlcp, psHeader, psSequence, psInfo, phFriNfc_LlcpTransport_Send_CB, (void*)LlcpTransport);
if (status != NFCSTATUS_PENDING && status != NFCSTATUS_SUCCESS) {
// Clear out callbacks
LlcpTransport->pfLinkSendCb = NULL;
LlcpTransport->pLinkSendContext = NULL;
}
return status;
}
/* TODO: comment function Transport SendFrameReject */
NFCSTATUS phFriNfc_LlcpTransport_SendFrameReject(phFriNfc_LlcpTransport_t *psTransport,
uint8_t dsap,
uint8_t rejectedPTYPE,
uint8_t ssap,
phFriNfc_Llcp_sPacketSequence_t* sLlcpSequence,
uint8_t WFlag,
uint8_t IFlag,
uint8_t RFlag,
uint8_t SFlag,
uint8_t vs,
uint8_t vsa,
uint8_t vr,
uint8_t vra)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
phNfc_sData_t sFrmrBuffer;
uint8_t flagValue;
uint8_t sequence = 0;
uint8_t index;
uint8_t socketFound = FALSE;
/* Search a socket waiting for a FRAME */
for(index=0;index<PHFRINFC_LLCP_NB_SOCKET_MAX;index++)
{
/* Test if the socket is in connected state and if its SSAP and DSAP are valid */
if(psTransport->pSocketTable[index].socket_sSap == dsap
&& psTransport->pSocketTable[index].socket_dSap == ssap)
{
/* socket found */
socketFound = TRUE;
break;
}
}
/* Test if a socket has been found */
if(socketFound)
{
/* Set socket state to disconnected */
psTransport->pSocketTable[index].eSocket_State = phFriNfc_LlcpTransportSocket_eSocketDefault;
/* Call ErrCB due to a FRMR*/
psTransport->pSocketTable[index].pSocketErrCb( psTransport->pSocketTable[index].pContext,PHFRINFC_LLCP_ERR_FRAME_REJECTED);
/* Close the socket */
status = phFriNfc_LlcpTransport_ConnectionOriented_Close(&psTransport->pSocketTable[index]);
/* Set FRMR Header */
psTransport->sLlcpHeader.dsap = ssap;
psTransport->sLlcpHeader.ptype = PHFRINFC_LLCP_PTYPE_FRMR;
psTransport->sLlcpHeader.ssap = dsap;
/* Set FRMR Information Field */
flagValue = (WFlag<<7) | (IFlag<<6) | (RFlag<<5) | (SFlag<<4) | rejectedPTYPE;
if (sLlcpSequence != NULL)
{
sequence = (uint8_t)((sLlcpSequence->ns<<4)|(sLlcpSequence->nr));
}
psTransport->FrmrInfoBuffer[0] = flagValue;
psTransport->FrmrInfoBuffer[1] = sequence;
psTransport->FrmrInfoBuffer[2] = (vs<<4)|vr ;
psTransport->FrmrInfoBuffer[3] = (vsa<<4)|vra ;
/* Test if a send is pending */
if(testAndSetSendPending(psTransport))
{
psTransport->bFrmrPending = TRUE;
status = NFCSTATUS_PENDING;
}
else
{
sFrmrBuffer.buffer = psTransport->FrmrInfoBuffer;
sFrmrBuffer.length = 0x04; /* Size of FRMR Information field */
/* Send FRMR frame */
status = phFriNfc_Llcp_Send(psTransport->pLlcp,
&psTransport->sLlcpHeader,
NULL,
&sFrmrBuffer,
phFriNfc_LlcpTransport_Send_CB,
psTransport);
}
}
else
{
/* No active socket*/
/* FRMR Frame not handled*/
}
return status;
}
/* TODO: comment function Transport SendDisconnectMode (NOTE: used only
* for requests not bound to a socket, like "service not found")
*/
NFCSTATUS phFriNfc_LlcpTransport_SendDisconnectMode(phFriNfc_LlcpTransport_t* psTransport,
uint8_t dsap,
uint8_t ssap,
uint8_t dmOpCode)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Test if a send is pending */
if(testAndSetSendPending(psTransport))
{
/* DM pending */
psTransport->bDmPending = TRUE;
/* Store DM Info */
psTransport->DmInfoBuffer[0] = dsap;
psTransport->DmInfoBuffer[1] = ssap;
psTransport->DmInfoBuffer[2] = dmOpCode;
status = NFCSTATUS_PENDING;
}
else
{
/* Set the header */
psTransport->sDmHeader.dsap = dsap;
psTransport->sDmHeader.ptype = PHFRINFC_LLCP_PTYPE_DM;
psTransport->sDmHeader.ssap = ssap;
/* Save Operation Code to be provided in DM frame payload */
psTransport->DmInfoBuffer[2] = dmOpCode;
psTransport->sDmPayload.buffer = &psTransport->DmInfoBuffer[2];
psTransport->sDmPayload.length = PHFRINFC_LLCP_DM_LENGTH;
/* Send DM frame */
status = phFriNfc_Llcp_Send(psTransport->pLlcp,
&psTransport->sDmHeader,
NULL,
&psTransport->sDmPayload,
phFriNfc_LlcpTransport_Send_CB,
psTransport);
}
return status;
}
/**
* \ingroup grp_lib_nfc
* \brief <b>Get the local options of a socket</b>.
*
* This function returns the local options (maximum packet size and receive window size) used
* for a given connection-oriented socket. This function shall not be used with connectionless
* sockets.
*
* \param[out] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] psLocalOptions A pointer to be filled with the local options of the socket.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_NOT_INITIALISED Indicates stack is not yet initialized.
* \retval NFCSTATUS_SHUTDOWN Shutdown in progress.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_SocketGetLocalOptions(phFriNfc_LlcpTransport_Socket_t *pLlcpSocket,
phLibNfc_Llcp_sSocketOptions_t *psLocalOptions)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Check for NULL pointers */
if (pLlcpSocket == NULL || psLocalOptions == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test the socket type */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test the socket state */
else if(pLlcpSocket->eSocket_State == phFriNfc_LlcpTransportSocket_eSocketDefault)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
else
{
status = phFriNfc_LlcpTransport_ConnectionOriented_SocketGetLocalOptions(pLlcpSocket,
psLocalOptions);
}
return status;
}
/**
* \ingroup grp_lib_nfc
* \brief <b>Get the local options of a socket</b>.
*
* This function returns the remote options (maximum packet size and receive window size) used
* for a given connection-oriented socket. This function shall not be used with connectionless
* sockets.
*
* \param[out] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] psRemoteOptions A pointer to be filled with the remote options of the socket.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_NOT_INITIALISED Indicates stack is not yet initialized.
* \retval NFCSTATUS_SHUTDOWN Shutdown in progress.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_SocketGetRemoteOptions(phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
phLibNfc_Llcp_sSocketOptions_t* psRemoteOptions)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Check for NULL pointers */
if (pLlcpSocket == NULL || psRemoteOptions == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test the socket type */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test the socket state */
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketConnected)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
else
{
status = phFriNfc_LlcpTransport_ConnectionOriented_SocketGetRemoteOptions(pLlcpSocket,
psRemoteOptions);
}
return status;
}
static NFCSTATUS phFriNfc_LlcpTransport_DiscoverServicesEx(phFriNfc_LlcpTransport_t *psTransport)
{
NFCSTATUS result = NFCSTATUS_PENDING;
phNfc_sData_t sInfoBuffer;
phNfc_sData_t *psServiceName;
uint32_t nTlvOffset;
/* Test if a send is pending */
if(!testAndSetSendPending(psTransport))
{
/* Set the header */
psTransport->sLlcpHeader.dsap = PHFRINFC_LLCP_SAP_SDP;
psTransport->sLlcpHeader.ptype = PHFRINFC_LLCP_PTYPE_SNL;
psTransport->sLlcpHeader.ssap = PHFRINFC_LLCP_SAP_SDP;
/* Prepare the info buffer */
sInfoBuffer.buffer = psTransport->pDiscoveryBuffer;
sInfoBuffer.length = sizeof(psTransport->pDiscoveryBuffer);
/* Encode as many requests as possible */
nTlvOffset = 0;
while(psTransport->nDiscoveryReqOffset < psTransport->nDiscoveryListSize)
{
/* Get current service name and try to encode it in SNL frame */
psServiceName = &psTransport->psDiscoveryServiceNameList[psTransport->nDiscoveryReqOffset];
result = phFriNfc_LlcpTransport_EncodeSdreqTlv(&sInfoBuffer,
&nTlvOffset,
psTransport->nDiscoveryReqOffset,
psServiceName);
if (result != NFCSTATUS_SUCCESS)
{
/* Impossible to fit more requests in a single frame,
* will be continued on next opportunity
*/
break;
}
/* Update request counter */
psTransport->nDiscoveryReqOffset++;
}
/* Update buffer length to match real TLV size */
sInfoBuffer.length = nTlvOffset;
/* Send SNL frame */
result = phFriNfc_Llcp_Send(psTransport->pLlcp,
&psTransport->sLlcpHeader,
NULL,
&sInfoBuffer,
phFriNfc_LlcpTransport_Send_CB,
psTransport);
}
else
{
/* Impossible to send now, this function will be called again on next opportunity */
}
return result;
}
/*!
* \ingroup grp_fri_nfc
* \brief <b>Discover remote services SAP using SDP protocol</b>.
*/
NFCSTATUS phFriNfc_LlcpTransport_DiscoverServices( phFriNfc_LlcpTransport_t *pLlcpTransport,
phNfc_sData_t *psServiceNameList,
uint8_t *pnSapList,
uint8_t nListSize,
pphFriNfc_Cr_t pDiscover_Cb,
void *pContext )
{
NFCSTATUS result = NFCSTATUS_FAILED;
/* Save request details */
pLlcpTransport->psDiscoveryServiceNameList = psServiceNameList;
pLlcpTransport->pnDiscoverySapList = pnSapList;
pLlcpTransport->nDiscoveryListSize = nListSize;
pLlcpTransport->pfDiscover_Cb = pDiscover_Cb;
pLlcpTransport->pDiscoverContext = pContext;
/* Reset internal counters */
pLlcpTransport->nDiscoveryReqOffset = 0;
pLlcpTransport->nDiscoveryResOffset = 0;
/* Perform request */
result = phFriNfc_LlcpTransport_DiscoverServicesEx(pLlcpTransport);
return result;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Create a socket on a LLCP-connected device</b>.
*
* This function creates a socket for a given LLCP link. Sockets can be of two types :
* connection-oriented and connectionless. If the socket is connection-oriented, the caller
* must provide a working buffer to the socket in order to handle incoming data. This buffer
* must be large enough to fit the receive window (RW * MIU), the remaining space being
* used as a linear buffer to store incoming data as a stream. Data will be readable later
* using the phLibNfc_LlcpTransport_Recv function.
* The options and working buffer are not required if the socket is used as a listening socket,
* since it cannot be directly used for communication.
*
* \param[in] pLlcpSocketTable A pointer to a table of PHFRINFC_LLCP_NB_SOCKET_DEFAULT sockets.
* \param[in] eType The socket type.
* \param[in] psOptions The options to be used with the socket.
* \param[in] psWorkingBuffer A working buffer to be used by the library.
* \param[out] pLlcpSocket A pointer on the socket to be filled with a
socket found on the socket table.
* \param[in] pErr_Cb The callback to be called each time the socket
* is in error.
* \param[in] pContext Upper layer context to be returned in the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_BUFFER_TOO_SMALL The working buffer is too small for the MIU and RW
* declared in the options.
* \retval NFCSTATUS_INSUFFICIENT_RESOURCES No more socket handle available.
* \retval NFCSTATUS_FAILED Operation failed.
* */
NFCSTATUS phFriNfc_LlcpTransport_Socket(phFriNfc_LlcpTransport_t *pLlcpTransport,
phFriNfc_LlcpTransport_eSocketType_t eType,
phFriNfc_LlcpTransport_sSocketOptions_t *psOptions,
phNfc_sData_t *psWorkingBuffer,
phFriNfc_LlcpTransport_Socket_t **pLlcpSocket,
pphFriNfc_LlcpTransportSocketErrCb_t pErr_Cb,
void *pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
phFriNfc_Llcp_sLinkParameters_t LlcpLinkParamInfo;
uint8_t index=0;
uint8_t cpt;
/* Check for NULL pointers */
if ( ((psOptions == NULL) && (eType == phFriNfc_LlcpTransport_eConnectionOriented))
|| ((psWorkingBuffer == NULL) && (eType == phFriNfc_LlcpTransport_eConnectionOriented))
|| (pLlcpSocket == NULL)
|| (pErr_Cb == NULL)
|| (pContext == NULL)
|| (pLlcpTransport == NULL))
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
return status;
}
/* Test the socket type*/
else if(eType != phFriNfc_LlcpTransport_eConnectionOriented && eType != phFriNfc_LlcpTransport_eConnectionLess)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
return status;
}
/* Connectionless sockets don't support options */
else if ((psOptions != NULL) && (eType == phFriNfc_LlcpTransport_eConnectionLess))
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
return status;
}
/* Get the local parameters of the LLCP Link */
status = phFriNfc_Llcp_GetLocalInfo(pLlcpTransport->pLlcp,&LlcpLinkParamInfo);
if(status != NFCSTATUS_SUCCESS)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_FAILED);
return status;
}
else
{
/* Search a socket free in the Socket Table*/
do
{
if(pLlcpTransport->pSocketTable[index].eSocket_State == phFriNfc_LlcpTransportSocket_eSocketDefault)
{
/* Set the socket pointer to socket of the table */
*pLlcpSocket = &pLlcpTransport->pSocketTable[index];
/* Store the socket info in the socket pointer */
pLlcpTransport->pSocketTable[index].eSocket_Type = eType;
pLlcpTransport->pSocketTable[index].pSocketErrCb = pErr_Cb;
/* Store the context of the upper layer */
pLlcpTransport->pSocketTable[index].pContext = pContext;
/* Set the pointers to the different working buffers */
if (eType == phFriNfc_LlcpTransport_eConnectionOriented)
{
/* Test the socket options */
if (psOptions->rw > PHFRINFC_LLCP_RW_MAX)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
return status;
}
/* Set socket options */
memcpy(&pLlcpTransport->pSocketTable[index].sSocketOption, psOptions, sizeof(phFriNfc_LlcpTransport_sSocketOptions_t));
/* Set socket local params (MIUX & RW) */
pLlcpTransport->pSocketTable[index].localMIUX = (pLlcpTransport->pSocketTable[index].sSocketOption.miu - PHFRINFC_LLCP_MIU_DEFAULT) & PHFRINFC_LLCP_TLV_MIUX_MASK;
pLlcpTransport->pSocketTable[index].localRW = pLlcpTransport->pSocketTable[index].sSocketOption.rw & PHFRINFC_LLCP_TLV_RW_MASK;
/* Set the Max length for the Send and Receive Window Buffer */
pLlcpTransport->pSocketTable[index].bufferSendMaxLength = pLlcpTransport->pSocketTable[index].sSocketOption.miu;
pLlcpTransport->pSocketTable[index].bufferRwMaxLength = pLlcpTransport->pSocketTable[index].sSocketOption.miu * ((pLlcpTransport->pSocketTable[index].sSocketOption.rw & PHFRINFC_LLCP_TLV_RW_MASK));
pLlcpTransport->pSocketTable[index].bufferLinearLength = psWorkingBuffer->length - pLlcpTransport->pSocketTable[index].bufferSendMaxLength - pLlcpTransport->pSocketTable[index].bufferRwMaxLength;
/* Test the connection oriented buffers length */
if((pLlcpTransport->pSocketTable[index].bufferSendMaxLength + pLlcpTransport->pSocketTable[index].bufferRwMaxLength) > psWorkingBuffer->length
|| ((pLlcpTransport->pSocketTable[index].bufferLinearLength < PHFRINFC_LLCP_MIU_DEFAULT) && (pLlcpTransport->pSocketTable[index].bufferLinearLength != 0)))
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_BUFFER_TOO_SMALL);
return status;
}
/* Set the pointer and the length for the Receive Window Buffer */
for(cpt=0;cpt<pLlcpTransport->pSocketTable[index].localRW;cpt++)
{
pLlcpTransport->pSocketTable[index].sSocketRwBufferTable[cpt].buffer = psWorkingBuffer->buffer + (cpt*pLlcpTransport->pSocketTable[index].sSocketOption.miu);
pLlcpTransport->pSocketTable[index].sSocketRwBufferTable[cpt].length = 0;
}
/* Set the pointer and the length for the Send Buffer */
pLlcpTransport->pSocketTable[index].sSocketSendBuffer.buffer = psWorkingBuffer->buffer + pLlcpTransport->pSocketTable[index].bufferRwMaxLength;
pLlcpTransport->pSocketTable[index].sSocketSendBuffer.length = pLlcpTransport->pSocketTable[index].bufferSendMaxLength;
/** Set the pointer and the length for the Linear Buffer */
pLlcpTransport->pSocketTable[index].sSocketLinearBuffer.buffer = psWorkingBuffer->buffer + pLlcpTransport->pSocketTable[index].bufferRwMaxLength + pLlcpTransport->pSocketTable[index].bufferSendMaxLength;
pLlcpTransport->pSocketTable[index].sSocketLinearBuffer.length = pLlcpTransport->pSocketTable[index].bufferLinearLength;
if(pLlcpTransport->pSocketTable[index].sSocketLinearBuffer.length != 0)
{
/* Init Cyclic Fifo */
phFriNfc_Llcp_CyclicFifoInit(&pLlcpTransport->pSocketTable[index].sCyclicFifoBuffer,
pLlcpTransport->pSocketTable[index].sSocketLinearBuffer.buffer,
pLlcpTransport->pSocketTable[index].sSocketLinearBuffer.length);
}
}
/* Handle connectionless socket with buffering option */
else if (eType == phFriNfc_LlcpTransport_eConnectionLess)
{
/* Determine how many packets can be bufferized in working buffer */
if (psWorkingBuffer != NULL)
{
/* NOTE: the extra byte is used to store SSAP */
pLlcpTransport->pSocketTable[index].localRW = psWorkingBuffer->length / (pLlcpTransport->pLlcp->sLocalParams.miu + 1);
}
else
{
pLlcpTransport->pSocketTable[index].localRW = 0;
}
if (pLlcpTransport->pSocketTable[index].localRW > PHFRINFC_LLCP_RW_MAX)
{
pLlcpTransport->pSocketTable[index].localRW = PHFRINFC_LLCP_RW_MAX;
}
/* Set the pointers and the lengths for buffering */
for(cpt=0 ; cpt<pLlcpTransport->pSocketTable[index].localRW ; cpt++)
{
pLlcpTransport->pSocketTable[index].sSocketRwBufferTable[cpt].buffer = psWorkingBuffer->buffer + (cpt*(pLlcpTransport->pLlcp->sLocalParams.miu + 1));
pLlcpTransport->pSocketTable[index].sSocketRwBufferTable[cpt].length = 0;
}
/* Set other socket internals */
pLlcpTransport->pSocketTable[index].indexRwRead = 0;
pLlcpTransport->pSocketTable[index].indexRwWrite = 0;
}
/* Store index of the socket */
pLlcpTransport->pSocketTable[index].index = index;
/* Set the socket into created state */
pLlcpTransport->pSocketTable[index].eSocket_State = phFriNfc_LlcpTransportSocket_eSocketCreated;
return status;
}
else
{
index++;
}
}while(index<PHFRINFC_LLCP_NB_SOCKET_MAX);
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INSUFFICIENT_RESOURCES);
}
return status;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Close a socket on a LLCP-connected device</b>.
*
* This function closes a LLCP socket previously created using phFriNfc_LlcpTransport_Socket.
* If the socket was connected, it is first disconnected, and then closed.
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_Close(phFriNfc_LlcpTransport_Socket_t* pLlcpSocket)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Check for NULL pointers */
if( pLlcpSocket == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else if(pLlcpSocket->eSocket_Type == phFriNfc_LlcpTransport_eConnectionOriented)
{
status = phFriNfc_LlcpTransport_ConnectionOriented_Close(pLlcpSocket);
}
else if(pLlcpSocket->eSocket_Type == phFriNfc_LlcpTransport_eConnectionLess)
{
status = phFriNfc_LlcpTransport_Connectionless_Close(pLlcpSocket);
}
else
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
return status;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Bind a socket to a local SAP</b>.
*
* This function binds the socket to a local Service Access Point.
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] nSap The SAP number to bind with, or 0 for auto-bind to a free SAP.
* \param[in] psServiceName A pointer to Service Name, or NULL if no service name.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_ALREADY_REGISTERED The selected SAP is already bound to another
socket.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_Bind(phFriNfc_LlcpTransport_Socket_t *pLlcpSocket,
uint8_t nSap,
phNfc_sData_t *psServiceName)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
uint8_t i;
uint8_t min_sap_range;
uint8_t max_sap_range;
/* Check for NULL pointers */
if(pLlcpSocket == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketCreated)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
else
{
/* Calculate authorized SAP range */
if ((psServiceName != NULL) && (psServiceName->length > 0))
{
/* SDP advertised service */
min_sap_range = PHFRINFC_LLCP_SAP_SDP_ADVERTISED_FIRST;
max_sap_range = PHFRINFC_LLCP_SAP_SDP_UNADVERTISED_FIRST;
}
else
{
/* Non-SDP advertised service */
min_sap_range = PHFRINFC_LLCP_SAP_SDP_UNADVERTISED_FIRST;
max_sap_range = PHFRINFC_LLCP_SAP_NUMBER;
}
/* Handle dynamic SAP allocation */
if (nSap == 0)
{
status = phFriNfc_LlcpTransport_GetFreeSap(pLlcpSocket->psTransport, psServiceName, &nSap);
if (status != NFCSTATUS_SUCCESS)
{
return status;
}
}
/* Test the SAP range */
if(!IS_BETWEEN(nSap, min_sap_range, max_sap_range) &&
!IS_BETWEEN(nSap, PHFRINFC_LLCP_SAP_WKS_FIRST, PHFRINFC_LLCP_SAP_SDP_ADVERTISED_FIRST))
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else
{
/* Test if the nSap it is used by another socket */
for(i=0;i<PHFRINFC_LLCP_NB_SOCKET_MAX;i++)
{
if(pLlcpSocket->psTransport->pSocketTable[i].socket_sSap == nSap)
{
return status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_ALREADY_REGISTERED);
}
}
/* Set service name */
status = phFriNfc_LlcpTransport_RegisterName(pLlcpSocket, nSap, psServiceName);
if (status != NFCSTATUS_SUCCESS)
{
return status;
}
/* Set the nSap value of the socket */
pLlcpSocket->socket_sSap = nSap;
/* Set the socket state */
pLlcpSocket->eSocket_State = phFriNfc_LlcpTransportSocket_eSocketBound;
}
}
return status;
}
/*********************************************/
/* ConnectionOriented */
/*********************************************/
/**
* \ingroup grp_fri_nfc
* \brief <b>Listen for incoming connection requests on a socket</b>.
*
* This function switches a socket into a listening state and registers a callback on
* incoming connection requests. In this state, the socket is not able to communicate
* directly. The listening state is only available for connection-oriented sockets
* which are still not connected. The socket keeps listening until it is closed, and
* thus can trigger several times the pListen_Cb callback.
*
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] pListen_Cb The callback to be called each time the
* socket receive a connection request.
* \param[in] pContext Upper layer context to be returned in
* the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state to switch
* to listening state.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_Listen(phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
pphFriNfc_LlcpTransportSocketListenCb_t pListen_Cb,
void* pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Check for NULL pointers */
if(pLlcpSocket == NULL || pListen_Cb == NULL|| pContext == NULL )
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Check for socket state */
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketBound)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
/* Check for socket type */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if a listen is not pending with this socket */
else if(pLlcpSocket->bSocketListenPending)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else
{
status = phFriNfc_LlcpTransport_ConnectionOriented_Listen(pLlcpSocket,
pListen_Cb,
pContext);
}
return status;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Register the socket service name</b>.
*
* This function changes the service name of the corresponding socket.
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] nSap SAP number associated to the service name.
* \param[in] psServiceName A pointer to a Service Name.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_FAILED Operation failed.
*/
static NFCSTATUS phFriNfc_LlcpTransport_RegisterName(phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
uint8_t nSap,
phNfc_sData_t *psServiceName)
{
phFriNfc_LlcpTransport_t * psTransport = pLlcpSocket->psTransport;
uint8_t index;
uint8_t bSnMatch, bSapMatch;
/* Check in cache if sap has been used for different service name */
for(index=0 ; index<PHFRINFC_LLCP_SDP_ADVERTISED_NB ; index++)
{
if(psTransport->pCachedServiceNames[index].sServiceName.length == 0)
{
/* Reached end of table */
break;
}
bSnMatch = (memcmp(psTransport->pCachedServiceNames[index].sServiceName.buffer, psServiceName->buffer, psServiceName->length) == 0);
bSapMatch = psTransport->pCachedServiceNames[index].nSap == nSap;
if(bSnMatch && bSapMatch)
{
/* Request match cache */
break;
}
else if((bSnMatch && !bSapMatch) || (!bSnMatch && bSapMatch))
{
/* Request mismatch with cache */
return NFCSTATUS_INVALID_PARAMETER;
}
}
/* Handle service with no name */
if (psServiceName == NULL)
{
if (pLlcpSocket->sServiceName.buffer != NULL)
{
phOsalNfc_FreeMemory(pLlcpSocket->sServiceName.buffer);
}
pLlcpSocket->sServiceName.buffer = NULL;
pLlcpSocket->sServiceName.length = 0;
}
else
{
/* Check if name already in use */
for(index=0;index<PHFRINFC_LLCP_NB_SOCKET_MAX;index++)
{
phFriNfc_LlcpTransport_Socket_t* pCurrentSocket = &pLlcpSocket->psTransport->pSocketTable[index];
if( (pCurrentSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketBound)
&& (pCurrentSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketRegistered))
{
/* Only bound or listening sockets may have a service name */
continue;
}
if(pCurrentSocket->sServiceName.length != psServiceName->length) {
/* Service name do not match, check next */
continue;
}
if(memcmp(pCurrentSocket->sServiceName.buffer, psServiceName->buffer, psServiceName->length) == 0)
{
/* Service name already in use */
return NFCSTATUS_INVALID_PARAMETER;
}
}
/* Store the listen socket SN */
pLlcpSocket->sServiceName.length = psServiceName->length;
pLlcpSocket->sServiceName.buffer = phOsalNfc_GetMemory(psServiceName->length);
if (pLlcpSocket->sServiceName.buffer == NULL)
{
return NFCSTATUS_NOT_ENOUGH_MEMORY;
}
memcpy(pLlcpSocket->sServiceName.buffer, psServiceName->buffer, psServiceName->length);
}
return NFCSTATUS_SUCCESS;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Accept an incoming connection request for a socket</b>.
*
* This functions allows the client to accept an incoming connection request.
* It must be used with the socket provided within the listen callback. The socket
* is implicitly switched to the connected state when the function is called.
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] psOptions The options to be used with the socket.
* \param[in] psWorkingBuffer A working buffer to be used by the library.
* \param[in] pErr_Cb The callback to be called each time the accepted socket
* is in error.
* \param[in] pContext Upper layer context to be returned in the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_BUFFER_TOO_SMALL The working buffer is too small for the MIU and RW
* declared in the options.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_Accept(phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
phFriNfc_LlcpTransport_sSocketOptions_t* psOptions,
phNfc_sData_t* psWorkingBuffer,
pphFriNfc_LlcpTransportSocketErrCb_t pErr_Cb,
pphFriNfc_LlcpTransportSocketAcceptCb_t pAccept_RspCb,
void* pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Check for NULL pointers */
if(pLlcpSocket == NULL || psOptions == NULL || psWorkingBuffer == NULL || pErr_Cb == NULL || pContext == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Check for socket state */
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketBound)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
/* Check for socket type */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test the socket options */
else if(psOptions->rw > PHFRINFC_LLCP_RW_MAX)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else
{
/* Set the Max length for the Send and Receive Window Buffer */
pLlcpSocket->bufferSendMaxLength = psOptions->miu;
pLlcpSocket->bufferRwMaxLength = psOptions->miu * ((psOptions->rw & PHFRINFC_LLCP_TLV_RW_MASK));
pLlcpSocket->bufferLinearLength = psWorkingBuffer->length - pLlcpSocket->bufferSendMaxLength - pLlcpSocket->bufferRwMaxLength;
/* Test the buffers length */
if((pLlcpSocket->bufferSendMaxLength + pLlcpSocket->bufferRwMaxLength) > psWorkingBuffer->length
|| ((pLlcpSocket->bufferLinearLength < PHFRINFC_LLCP_MIU_DEFAULT) && (pLlcpSocket->bufferLinearLength != 0)))
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_BUFFER_TOO_SMALL);
}
else
{
pLlcpSocket->psTransport->socketIndex = pLlcpSocket->index;
status = phFriNfc_LlcpTransport_ConnectionOriented_Accept(pLlcpSocket,
psOptions,
psWorkingBuffer,
pErr_Cb,
pAccept_RspCb,
pContext);
}
}
return status;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Reject an incoming connection request for a socket</b>.
*
* This functions allows the client to reject an incoming connection request.
* It must be used with the socket provided within the listen callback. The socket
* is implicitly closed when the function is called.
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] pReject_RspCb The callback to be call when the Reject operation is completed
* \param[in] pContext Upper layer context to be returned in the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_Reject( phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
pphFriNfc_LlcpTransportSocketRejectCb_t pReject_RspCb,
void *pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Check for NULL pointers */
if(pLlcpSocket == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Check for socket state */
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketBound)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
/* Check for socket type */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else
{
status = phLibNfc_LlcpTransport_ConnectionOriented_Reject(pLlcpSocket,
pReject_RspCb,
pContext);
}
return status;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Try to establish connection with a socket on a remote SAP</b>.
*
* This function tries to connect to a given SAP on the remote peer. If the
* socket is not bound to a local SAP, it is implicitly bound to a free SAP.
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] nSap The destination SAP to connect to.
* \param[in] pConnect_RspCb The callback to be called when the connection
* operation is completed.
* \param[in] pContext Upper layer context to be returned in
* the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_PENDING Connection operation is in progress,
* pConnect_RspCb will be called upon completion.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_Connect( phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
uint8_t nSap,
pphFriNfc_LlcpTransportSocketConnectCb_t pConnect_RspCb,
void* pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
uint8_t nLocalSap;
uint8_t i;
/* Check for NULL pointers */
if(pLlcpSocket == NULL || pConnect_RspCb == NULL || pContext == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test the port number value */
else if(nSap<02 || nSap>63)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is a connectionOriented socket */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket has a service name */
else if(pLlcpSocket->sServiceName.length != 0)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
/* Test if the socket is not in connecting or connected state*/
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketCreated && pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketBound)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
else
{
/* Implicit bind if socket is not already bound */
if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketBound)
{
/* Bind to a free sap */
status = phFriNfc_LlcpTransport_GetFreeSap(pLlcpSocket->psTransport, NULL, &nLocalSap);
if (status != NFCSTATUS_SUCCESS)
{
return status;
}
pLlcpSocket->socket_sSap = nLocalSap;
}
/* Test the SAP range for non SDP-advertised services */
if(!IS_BETWEEN(pLlcpSocket->socket_sSap, PHFRINFC_LLCP_SAP_SDP_UNADVERTISED_FIRST, PHFRINFC_LLCP_SAP_NUMBER))
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else
{
status = phFriNfc_LlcpTransport_ConnectionOriented_Connect(pLlcpSocket,
nSap,
NULL,
pConnect_RspCb,
pContext);
}
}
return status;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Try to establish connection with a socket on a remote service, given its URI</b>.
*
* This function tries to connect to a SAP designated by an URI. If the
* socket is not bound to a local SAP, it is implicitly bound to a free SAP.
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] psUri The URI corresponding to the destination SAP to connect to.
* \param[in] pConnect_RspCb The callback to be called when the connection
* operation is completed.
* \param[in] pContext Upper layer context to be returned in
* the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_PENDING Connection operation is in progress,
* pConnect_RspCb will be called upon completion.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_NOT_INITIALISED Indicates stack is not yet initialized.
* \retval NFCSTATUS_SHUTDOWN Shutdown in progress.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_ConnectByUri(phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
phNfc_sData_t* psUri,
pphFriNfc_LlcpTransportSocketConnectCb_t pConnect_RspCb,
void* pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
uint8_t i;
uint8_t nLocalSap;
/* Check for NULL pointers */
if(pLlcpSocket == NULL || pConnect_RspCb == NULL || pContext == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is a connectionOriented socket */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is not in connect pending or connected state*/
else if(pLlcpSocket->eSocket_State == phFriNfc_LlcpTransportSocket_eSocketConnecting || pLlcpSocket->eSocket_State == phFriNfc_LlcpTransportSocket_eSocketConnected)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test the length of the SN */
else if(psUri->length > PHFRINFC_LLCP_SN_MAX_LENGTH)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else
{
/* Implicit bind if socket is not already bound */
if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketBound)
{
/* Bind to a free sap */
status = phFriNfc_LlcpTransport_GetFreeSap(pLlcpSocket->psTransport, NULL, &nLocalSap);
if (status != NFCSTATUS_SUCCESS)
{
return status;
}
pLlcpSocket->socket_sSap = nLocalSap;
}
/* Test the SAP range for non SDP-advertised services */
if(!IS_BETWEEN(pLlcpSocket->socket_sSap, PHFRINFC_LLCP_SAP_SDP_UNADVERTISED_FIRST, PHFRINFC_LLCP_SAP_NUMBER))
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else
{
status = phFriNfc_LlcpTransport_ConnectionOriented_Connect(pLlcpSocket,
PHFRINFC_LLCP_SAP_DEFAULT,
psUri,
pConnect_RspCb,
pContext);
}
}
return status;
}
/**
* \ingroup grp_lib_nfc
* \brief <b>Disconnect a currently connected socket</b>.
*
* This function initiates the disconnection of a previously connected socket.
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] pDisconnect_RspCb The callback to be called when the
* operation is completed.
* \param[in] pContext Upper layer context to be returned in
* the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_PENDING Disconnection operation is in progress,
* pDisconnect_RspCb will be called upon completion.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_NOT_INITIALISED Indicates stack is not yet initialized.
* \retval NFCSTATUS_SHUTDOWN Shutdown in progress.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_Disconnect(phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
pphLibNfc_LlcpSocketDisconnectCb_t pDisconnect_RspCb,
void* pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Check for NULL pointers */
if(pLlcpSocket == NULL || pDisconnect_RspCb == NULL || pContext == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is a connectionOriented socket */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is connected state*/
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketConnected)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
else
{
status = phLibNfc_LlcpTransport_ConnectionOriented_Disconnect(pLlcpSocket,
pDisconnect_RspCb,
pContext);
}
return status;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Send data on a socket</b>.
*
* This function is used to write data on a socket. This function
* can only be called on a connection-oriented socket which is already
* in a connected state.
*
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] psBuffer The buffer containing the data to send.
* \param[in] pSend_RspCb The callback to be called when the
* operation is completed.
* \param[in] pContext Upper layer context to be returned in
* the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_PENDING Reception operation is in progress,
* pSend_RspCb will be called upon completion.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_Send(phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
phNfc_sData_t* psBuffer,
pphFriNfc_LlcpTransportSocketSendCb_t pSend_RspCb,
void* pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Check for NULL pointers */
if(pLlcpSocket == NULL || psBuffer == NULL || pSend_RspCb == NULL || pContext == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is a connectionOriented socket */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is in connected state */
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketConnected)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
/* Test the length of the buffer */
else if(psBuffer->length > pLlcpSocket->remoteMIU )
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if a send is pending */
else if(pLlcpSocket->pfSocketSend_Cb != NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_REJECTED);
}
else
{
status = phFriNfc_LlcpTransport_ConnectionOriented_Send(pLlcpSocket,
psBuffer,
pSend_RspCb,
pContext);
}
return status;
}
/**
* \ingroup grp_fri_nfc
* \brief <b>Read data on a socket</b>.
*
* This function is used to read data from a socket. It reads at most the
* size of the reception buffer, but can also return less bytes if less bytes
* are available. If no data is available, the function will be pending until
* more data comes, and the response will be sent by the callback. This function
* can only be called on a connection-oriented socket.
*
*
* \param[in] pLlcpSocket A pointer to a phFriNfc_LlcpTransport_Socket_t.
* \param[in] psBuffer The buffer receiving the data.
* \param[in] pRecv_RspCb The callback to be called when the
* operation is completed.
* \param[in] pContext Upper layer context to be returned in
* the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_PENDING Reception operation is in progress,
* pRecv_RspCb will be called upon completion.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_Recv( phFriNfc_LlcpTransport_Socket_t* pLlcpSocket,
phNfc_sData_t* psBuffer,
pphFriNfc_LlcpTransportSocketRecvCb_t pRecv_RspCb,
void* pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
/* Check for NULL pointers */
if(pLlcpSocket == NULL || psBuffer == NULL || pRecv_RspCb == NULL || pContext == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is a connectionOriented socket */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionOriented)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is in connected state */
else if(pLlcpSocket->eSocket_State == phFriNfc_LlcpTransportSocket_eSocketDefault)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if a receive is pending */
else if(pLlcpSocket->bSocketRecvPending == TRUE)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_REJECTED);
}
else
{
status = phFriNfc_LlcpTransport_ConnectionOriented_Recv(pLlcpSocket,
psBuffer,
pRecv_RspCb,
pContext);
}
return status;
}
/*****************************************/
/* ConnectionLess */
/*****************************************/
/**
* \ingroup grp_fri_nfc
* \brief <b>Send data on a socket to a given destination SAP</b>.
*
* This function is used to write data on a socket to a given destination SAP.
* This function can only be called on a connectionless socket.
*
*
* \param[in] pLlcpSocket A pointer to a LlcpSocket created.
* \param[in] nSap The destination SAP.
* \param[in] psBuffer The buffer containing the data to send.
* \param[in] pSend_RspCb The callback to be called when the
* operation is completed.
* \param[in] pContext Upper layer context to be returned in
* the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_PENDING Reception operation is in progress,
* pSend_RspCb will be called upon completion.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_SendTo( phFriNfc_LlcpTransport_Socket_t *pLlcpSocket,
uint8_t nSap,
phNfc_sData_t *psBuffer,
pphFriNfc_LlcpTransportSocketSendCb_t pSend_RspCb,
void* pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
phFriNfc_Llcp_sLinkParameters_t LlcpRemoteLinkParamInfo;
if(pLlcpSocket == NULL || psBuffer == NULL || pSend_RspCb == NULL || pContext == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test the port number value */
else if(nSap<2 || nSap>63)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is a connectionless socket */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionLess)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is in an updated state */
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketBound)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
/* Test if a send is pending */
else if(pLlcpSocket->pfSocketSend_Cb != NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_REJECTED);
}
else
{
/* Get the local parameters of the LLCP Link */
status = phFriNfc_Llcp_GetRemoteInfo(pLlcpSocket->psTransport->pLlcp,&LlcpRemoteLinkParamInfo);
if(status != NFCSTATUS_SUCCESS)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_FAILED);
}
/* Test the length of the socket buffer for ConnectionLess mode*/
else if(psBuffer->length > LlcpRemoteLinkParamInfo.miu)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the link is in error state */
else if(pLlcpSocket->psTransport->LinkStatusError)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_REJECTED);
}
else
{
status = phFriNfc_LlcpTransport_Connectionless_SendTo(pLlcpSocket,
nSap,
psBuffer,
pSend_RspCb,
pContext);
}
}
return status;
}
/**
* \ingroup grp_lib_nfc
* \brief <b>Read data on a socket and get the source SAP</b>.
*
* This function is the same as phLibNfc_Llcp_Recv, except that the callback includes
* the source SAP. This functions can only be called on a connectionless socket.
*
*
* \param[in] pLlcpSocket A pointer to a LlcpSocket created.
* \param[in] psBuffer The buffer receiving the data.
* \param[in] pRecv_RspCb The callback to be called when the
* operation is completed.
* \param[in] pContext Upper layer context to be returned in
* the callback.
*
* \retval NFCSTATUS_SUCCESS Operation successful.
* \retval NFCSTATUS_INVALID_PARAMETER One or more of the supplied parameters
* could not be properly interpreted.
* \retval NFCSTATUS_PENDING Reception operation is in progress,
* pRecv_RspCb will be called upon completion.
* \retval NFCSTATUS_INVALID_STATE The socket is not in a valid state, or not of
* a valid type to perform the requsted operation.
* \retval NFCSTATUS_NOT_INITIALISED Indicates stack is not yet initialized.
* \retval NFCSTATUS_SHUTDOWN Shutdown in progress.
* \retval NFCSTATUS_FAILED Operation failed.
*/
NFCSTATUS phFriNfc_LlcpTransport_RecvFrom( phFriNfc_LlcpTransport_Socket_t *pLlcpSocket,
phNfc_sData_t* psBuffer,
pphFriNfc_LlcpTransportSocketRecvFromCb_t pRecv_Cb,
void* pContext)
{
NFCSTATUS status = NFCSTATUS_SUCCESS;
if(pLlcpSocket == NULL || psBuffer == NULL || pRecv_Cb == NULL || pContext == NULL)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is a connectionless socket */
else if(pLlcpSocket->eSocket_Type != phFriNfc_LlcpTransport_eConnectionLess)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_PARAMETER);
}
/* Test if the socket is in an updated state */
else if(pLlcpSocket->eSocket_State != phFriNfc_LlcpTransportSocket_eSocketBound)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_INVALID_STATE);
}
else
{
if(pLlcpSocket->bSocketRecvPending)
{
status = PHNFCSTVAL(CID_FRI_NFC_LLCP_TRANSPORT, NFCSTATUS_REJECTED);
}
else
{
status = phLibNfc_LlcpTransport_Connectionless_RecvFrom(pLlcpSocket,
psBuffer,
pRecv_Cb,
pContext);
}
}
return status;
}