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