summaryrefslogtreecommitdiffstats
path: root/src/phLibNfc_llcp.c
diff options
context:
space:
mode:
authorArnaud Ferir <arnaud.ferir@trusted-logic.com>2010-12-21 18:10:50 +0100
committerNick Pelly <npelly@google.com>2011-01-18 13:22:46 -0800
commit76270e7c32ec0c558907430e756712d5b68b417c (patch)
tree3365a0c4f24e4d413d40a5382a5931e6403b66f3 /src/phLibNfc_llcp.c
parent1ec4ace9cd3079abd7c0b30d0976591c8c0f441e (diff)
downloadexternal_libnfc-nxp-76270e7c32ec0c558907430e756712d5b68b417c.zip
external_libnfc-nxp-76270e7c32ec0c558907430e756712d5b68b417c.tar.gz
external_libnfc-nxp-76270e7c32ec0c558907430e756712d5b68b417c.tar.bz2
Allow LLCP socket creation before link establishment.
This enables to register a server socket prior to LLCP link establishement and avoid any race condition between the local server and the remote client. The modification implies a LLCP socket API change (added the device handle parameter in all LLCP API that can lead to active data exchange). The LLCP socket internal structures are now initialized during LLCP link parameter configuration step instead of LLCP compliancy check to save socket state independently from peer detection. Also removed unused socket states and added some traces. Change-Id: I961c48af4ca9ace68d41b9569dc1038bb2bbdc71
Diffstat (limited to 'src/phLibNfc_llcp.c')
-rw-r--r--src/phLibNfc_llcp.c229
1 files changed, 164 insertions, 65 deletions
diff --git a/src/phLibNfc_llcp.c b/src/phLibNfc_llcp.c
index 86b15cf..2f78ab6 100644
--- a/src/phLibNfc_llcp.c
+++ b/src/phLibNfc_llcp.c
@@ -147,7 +147,35 @@ NFCSTATUS phLibNfc_Mgt_SetLlcp_ConfigParams( phLibNfc_Llcp_sLinkParameters_t* pC
/* Set the P2P general bytes */
result = phLibNfc_Mgt_SetP2P_ConfigParams(&sNfcIPCfg, pConfigRspCb, pContext);
- return PHNFCSTATUS(result);
+ if (result != NFCSTATUS_PENDING)
+ {
+ return PHNFCSTATUS(result);
+ }
+
+ /* Resets the LLCP LLC component */
+ result = phFriNfc_Llcp_Reset( &gpphLibContext->llcp_cntx.sLlcpContext,
+ gpphLibContext->psOverHalCtxt,
+ pConfigInfo,
+ gpphLibContext->llcp_cntx.pRxBuffer,
+ sizeof(gpphLibContext->llcp_cntx.pRxBuffer),
+ gpphLibContext->llcp_cntx.pTxBuffer,
+ sizeof(gpphLibContext->llcp_cntx.pTxBuffer),
+ phLibNfc_Llcp_Link_Cb,
+ gpphLibContext);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return PHNFCSTATUS(result);
+ }
+
+ /* Resets the LLCP Transport component */
+ result = phFriNfc_LlcpTransport_Reset( &gpphLibContext->llcp_cntx.sLlcpTransportContext,
+ &gpphLibContext->llcp_cntx.sLlcpContext );
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return PHNFCSTATUS(result);
+ }
+
+ return NFCSTATUS_PENDING;
}
NFCSTATUS phLibNfc_Llcp_CheckLlcp( phLibNfc_Handle hRemoteDevice,
@@ -196,28 +224,8 @@ NFCSTATUS phLibNfc_Llcp_CheckLlcp( phLibNfc_Handle hRemoteDevice,
gpphLibContext->CBInfo.pClientLlcpLinkCb = pLink_Cb;
gpphLibContext->CBInfo.pClientLlcpLinkCntx = pContext;
- /* Resets the LLCP LLC component */
- result = phFriNfc_Llcp_Reset( &gpphLibContext->llcp_cntx.sLlcpContext,
- gpphLibContext->psOverHalCtxt,
- &gpphLibContext->llcp_cntx.sLocalParams,
- gpphLibContext->llcp_cntx.pRxBuffer,
- sizeof(gpphLibContext->llcp_cntx.pRxBuffer),
- gpphLibContext->llcp_cntx.pTxBuffer,
- sizeof(gpphLibContext->llcp_cntx.pTxBuffer),
- phLibNfc_Llcp_Link_Cb,
- gpphLibContext);
- if (result != NFCSTATUS_SUCCESS)
- {
- return PHNFCSTATUS(result);
- }
-
- /* Resets the LLCP Transport component */
- result = phFriNfc_LlcpTransport_Reset( &gpphLibContext->llcp_cntx.sLlcpTransportContext,
- &gpphLibContext->llcp_cntx.sLlcpContext );
- if (result != NFCSTATUS_SUCCESS)
- {
- return result;
- }
+ // DEBUG: Reset at least the state
+ gpphLibContext->llcp_cntx.sLlcpContext.state = 0;
/* Prepare callback */
gpphLibContext->CBInfo.pClientLlcpCheckRespCb = pCheckLlcp_RspCb;
@@ -421,19 +429,11 @@ NFCSTATUS phLibNfc_Llcp_GetLocalInfo( phLibNfc_Handle hRemoteDe
}
/* Parameters checking */
- if ((hRemoteDevice == 0) ||
- (pConfigInfo == NULL))
+ if (pConfigInfo == NULL)
{
return NFCSTATUS_INVALID_PARAMETER;
}
- /* Check device */
- result = static_CheckDevice(hRemoteDevice);
- if (result != NFCSTATUS_SUCCESS)
- {
- return result;
- }
-
/* Get local infos */
result = phFriNfc_Llcp_GetLocalInfo(&gpphLibContext->llcp_cntx.sLlcpContext, pConfigInfo);
@@ -473,8 +473,7 @@ NFCSTATUS phLibNfc_Llcp_GetRemoteInfo( phLibNfc_Handle hRemot
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_Socket( phLibNfc_Handle hRemoteDevice,
- phLibNfc_Llcp_eSocketType_t eType,
+NFCSTATUS phLibNfc_Llcp_Socket( phLibNfc_Llcp_eSocketType_t eType,
phLibNfc_Llcp_sSocketOptions_t* psOptions,
phNfc_sData_t* psWorkingBuffer,
phLibNfc_Handle* phSocket,
@@ -494,20 +493,12 @@ NFCSTATUS phLibNfc_Llcp_Socket( phLibNfc_Handle hRemoteDevice,
/* Parameters checking */
/* NOTE: Transport Layer test psOption and psWorkingBuffer value */
- if ((hRemoteDevice == 0) ||
- (phSocket == NULL) ||
+ if ((phSocket == NULL) ||
(pErr_Cb == NULL))
{
return NFCSTATUS_INVALID_PARAMETER;
}
- /* Check device */
- result = static_CheckDevice(hRemoteDevice);
- if (result != NFCSTATUS_SUCCESS)
- {
- return result;
- }
-
/* Get local infos */
result = phFriNfc_LlcpTransport_Socket(&gpphLibContext->llcp_cntx.sLlcpTransportContext,
eType,
@@ -542,6 +533,7 @@ NFCSTATUS phLibNfc_Llcp_Close( phLibNfc_Handle hSocket )
}
/* Get local infos */
+ /* TODO: if connected abort and close else close only */
result = phFriNfc_LlcpTransport_Close(psSocket);
return PHNFCSTATUS(result);
@@ -574,13 +566,16 @@ NFCSTATUS phLibNfc_Llcp_SocketGetLocalOptions( phLibNfc_Handle
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_SocketGetRemoteOptions( phLibNfc_Handle hSocket,
+NFCSTATUS phLibNfc_Llcp_SocketGetRemoteOptions( phLibNfc_Handle hRemoteDevice,
+ phLibNfc_Handle hSocket,
phLibNfc_Llcp_sSocketOptions_t* psRemoteOptions
)
{
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_SocketGetRemoteOptions");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -589,12 +584,20 @@ NFCSTATUS phLibNfc_Llcp_SocketGetRemoteOptions( phLibNfc_Handle
}
/* Parameters checking */
- if ((hSocket == 0) ||
+ if ((hRemoteDevice == 0) ||
+ (hSocket == 0) ||
(psRemoteOptions == NULL))
{
return NFCSTATUS_INVALID_PARAMETER;
}
+ /* Check device */
+ result = static_CheckDevice(hRemoteDevice);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return result;
+ }
+
/* Get remote infos */
result = phFriNfc_LlcpTransport_SocketGetRemoteOptions(psSocket, psRemoteOptions);
@@ -608,6 +611,8 @@ NFCSTATUS phLibNfc_Llcp_Bind( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_Bind");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -636,6 +641,8 @@ NFCSTATUS phLibNfc_Llcp_Listen( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_Listen");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -671,6 +678,8 @@ NFCSTATUS phLibNfc_Llcp_Accept( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_Accept");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -699,7 +708,8 @@ NFCSTATUS phLibNfc_Llcp_Accept( phLibNfc_Handle hSocket,
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_Reject( phLibNfc_Handle hSocket,
+NFCSTATUS phLibNfc_Llcp_Reject( phLibNfc_Handle hRemoteDevice,
+ phLibNfc_Handle hSocket,
pphLibNfc_LlcpSocketRejectCb_t pReject_RspCb,
void* pContext
)
@@ -707,6 +717,8 @@ NFCSTATUS phLibNfc_Llcp_Reject( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_Reject");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -715,12 +727,20 @@ NFCSTATUS phLibNfc_Llcp_Reject( phLibNfc_Handle hSocket,
}
/* Parameters checking */
- if ((hSocket == 0) ||
+ if ((hRemoteDevice == 0) ||
+ (hSocket == 0) ||
(pReject_RspCb == NULL))
{
return NFCSTATUS_INVALID_PARAMETER;
}
+ /* Check device */
+ result = static_CheckDevice(hRemoteDevice);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return result;
+ }
+
/* Reject incoming connection */
result = phFriNfc_LlcpTransport_Reject( psSocket,
pReject_RspCb,
@@ -729,7 +749,8 @@ NFCSTATUS phLibNfc_Llcp_Reject( phLibNfc_Handle hSocket,
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_Connect( phLibNfc_Handle hSocket,
+NFCSTATUS phLibNfc_Llcp_Connect( phLibNfc_Handle hRemoteDevice,
+ phLibNfc_Handle hSocket,
uint8_t nSap,
pphLibNfc_LlcpSocketConnectCb_t pConnect_RspCb,
void* pContext
@@ -738,6 +759,8 @@ NFCSTATUS phLibNfc_Llcp_Connect( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_Connect");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -746,12 +769,21 @@ NFCSTATUS phLibNfc_Llcp_Connect( phLibNfc_Handle hSocket,
}
/* Parameters checking */
- if ((hSocket == 0) ||
+ if ((hRemoteDevice == 0) ||
+ (hSocket == 0) ||
(pConnect_RspCb == NULL))
{
+ LLCP_PRINT("phLibNfc_Llcp_Connect NFCSTATUS_INVALID_PARAMETER");
return NFCSTATUS_INVALID_PARAMETER;
}
+ /* Check device */
+ result = static_CheckDevice(hRemoteDevice);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return result;
+ }
+
/* Try to connect on a remote service, given its SAP */
result = phFriNfc_LlcpTransport_Connect( psSocket,
nSap,
@@ -761,7 +793,8 @@ NFCSTATUS phLibNfc_Llcp_Connect( phLibNfc_Handle hSocket,
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_ConnectByUri( phLibNfc_Handle hSocket,
+NFCSTATUS phLibNfc_Llcp_ConnectByUri( phLibNfc_Handle hRemoteDevice,
+ phLibNfc_Handle hSocket,
phNfc_sData_t* psUri,
pphLibNfc_LlcpSocketConnectCb_t pConnect_RspCb,
void* pContext
@@ -770,6 +803,8 @@ NFCSTATUS phLibNfc_Llcp_ConnectByUri( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_ConnectByUri");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -778,13 +813,22 @@ NFCSTATUS phLibNfc_Llcp_ConnectByUri( phLibNfc_Handle hSocket,
}
/* Parameters checking */
- if ((hSocket == 0) ||
+ if ((hRemoteDevice == 0) ||
+ (hSocket == 0) ||
(psUri == NULL) ||
(pConnect_RspCb == NULL))
{
+ LLCP_PRINT("phLibNfc_Llcp_ConnectByUri NFCSTATUS_INVALID_PARAMETER");
return NFCSTATUS_INVALID_PARAMETER;
}
+ /* Check device */
+ result = static_CheckDevice(hRemoteDevice);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return result;
+ }
+
/* Try to connect on a remote service, using SDP */
result = phFriNfc_LlcpTransport_ConnectByUri( psSocket,
psUri,
@@ -794,7 +838,8 @@ NFCSTATUS phLibNfc_Llcp_ConnectByUri( phLibNfc_Handle hSocket,
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_Disconnect( phLibNfc_Handle hSocket,
+NFCSTATUS phLibNfc_Llcp_Disconnect( phLibNfc_Handle hRemoteDevice,
+ phLibNfc_Handle hSocket,
pphLibNfc_LlcpSocketDisconnectCb_t pDisconnect_RspCb,
void* pContext
)
@@ -802,6 +847,8 @@ NFCSTATUS phLibNfc_Llcp_Disconnect( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_Disconnect");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -810,12 +857,20 @@ NFCSTATUS phLibNfc_Llcp_Disconnect( phLibNfc_Handle hSocket,
}
/* Parameters checking */
- if ((hSocket == 0) ||
+ if ((hRemoteDevice == 0) ||
+ (hSocket == 0) ||
(pDisconnect_RspCb == NULL))
{
return NFCSTATUS_INVALID_PARAMETER;
}
+ /* Check device */
+ result = static_CheckDevice(hRemoteDevice);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return result;
+ }
+
/* Disconnect a logical link */
result = phFriNfc_LlcpTransport_Disconnect( psSocket,
pDisconnect_RspCb,
@@ -824,7 +879,8 @@ NFCSTATUS phLibNfc_Llcp_Disconnect( phLibNfc_Handle hSocket,
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_Recv( phLibNfc_Handle hSocket,
+NFCSTATUS phLibNfc_Llcp_Recv( phLibNfc_Handle hRemoteDevice,
+ phLibNfc_Handle hSocket,
phNfc_sData_t* psBuffer,
pphLibNfc_LlcpSocketRecvCb_t pRecv_RspCb,
void* pContext
@@ -833,6 +889,8 @@ NFCSTATUS phLibNfc_Llcp_Recv( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_Recv");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -841,13 +899,21 @@ NFCSTATUS phLibNfc_Llcp_Recv( phLibNfc_Handle hSocket,
}
/* Parameters checking */
- if ((hSocket == 0) ||
- (psBuffer == NULL) ||
+ if ((hRemoteDevice == 0) ||
+ (hSocket == 0) ||
+ (psBuffer == NULL) ||
(pRecv_RspCb == NULL))
{
return NFCSTATUS_INVALID_PARAMETER;
}
+ /* Check device */
+ result = static_CheckDevice(hRemoteDevice);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return result;
+ }
+
/* Receive data from the logical link */
result = phFriNfc_LlcpTransport_Recv( psSocket,
psBuffer,
@@ -857,7 +923,8 @@ NFCSTATUS phLibNfc_Llcp_Recv( phLibNfc_Handle hSocket,
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_RecvFrom( phLibNfc_Handle hSocket,
+NFCSTATUS phLibNfc_Llcp_RecvFrom( phLibNfc_Handle hRemoteDevice,
+ phLibNfc_Handle hSocket,
phNfc_sData_t* psBuffer,
pphLibNfc_LlcpSocketRecvFromCb_t pRecv_Cb,
void* pContext
@@ -866,6 +933,8 @@ NFCSTATUS phLibNfc_Llcp_RecvFrom( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_RecvFrom");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -874,13 +943,21 @@ NFCSTATUS phLibNfc_Llcp_RecvFrom( phLibNfc_Handle hSocket,
}
/* Parameters checking */
- if ((hSocket == 0) ||
- (psBuffer == NULL) ||
+ if ((hRemoteDevice == 0) ||
+ (hSocket == 0) ||
+ (psBuffer == NULL) ||
(pRecv_Cb == NULL))
{
return NFCSTATUS_INVALID_PARAMETER;
}
+ /* Check device */
+ result = static_CheckDevice(hRemoteDevice);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return result;
+ }
+
/* Receive data from the logical link */
result = phFriNfc_LlcpTransport_RecvFrom( psSocket,
psBuffer,
@@ -890,7 +967,8 @@ NFCSTATUS phLibNfc_Llcp_RecvFrom( phLibNfc_Handle hSocket,
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_Send( phLibNfc_Handle hSocket,
+NFCSTATUS phLibNfc_Llcp_Send( phLibNfc_Handle hRemoteDevice,
+ phLibNfc_Handle hSocket,
phNfc_sData_t* psBuffer,
pphLibNfc_LlcpSocketSendCb_t pSend_RspCb,
void* pContext
@@ -899,6 +977,8 @@ NFCSTATUS phLibNfc_Llcp_Send( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_Send");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -907,13 +987,21 @@ NFCSTATUS phLibNfc_Llcp_Send( phLibNfc_Handle hSocket,
}
/* Parameters checking */
- if ((hSocket == 0) ||
- (psBuffer == NULL) ||
+ if ((hRemoteDevice == 0) ||
+ (hSocket == 0) ||
+ (psBuffer == NULL) ||
(pSend_RspCb == NULL))
{
return NFCSTATUS_INVALID_PARAMETER;
}
+ /* Check device */
+ result = static_CheckDevice(hRemoteDevice);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return result;
+ }
+
/* Send data to the logical link */
result = phFriNfc_LlcpTransport_Send( psSocket,
psBuffer,
@@ -923,7 +1011,8 @@ NFCSTATUS phLibNfc_Llcp_Send( phLibNfc_Handle hSocket,
return PHNFCSTATUS(result);
}
-NFCSTATUS phLibNfc_Llcp_SendTo( phLibNfc_Handle hSocket,
+NFCSTATUS phLibNfc_Llcp_SendTo( phLibNfc_Handle hRemoteDevice,
+ phLibNfc_Handle hSocket,
uint8_t nSap,
phNfc_sData_t* psBuffer,
pphLibNfc_LlcpSocketSendCb_t pSend_RspCb,
@@ -933,6 +1022,8 @@ NFCSTATUS phLibNfc_Llcp_SendTo( phLibNfc_Handle hSocket,
NFCSTATUS result;
phFriNfc_LlcpTransport_Socket_t *psSocket = (phFriNfc_LlcpTransport_Socket_t*)hSocket;
+ LLCP_PRINT("phLibNfc_Llcp_SendTo");
+
/* State checking */
result = static_CheckState();
if (result != NFCSTATUS_SUCCESS)
@@ -941,13 +1032,21 @@ NFCSTATUS phLibNfc_Llcp_SendTo( phLibNfc_Handle hSocket,
}
/* Parameters checking */
- if ((hSocket == 0) ||
- (psBuffer == NULL) ||
+ if ((hRemoteDevice == 0) ||
+ (hSocket == 0) ||
+ (psBuffer == NULL) ||
(pSend_RspCb == NULL))
{
return NFCSTATUS_INVALID_PARAMETER;
}
+ /* Check device */
+ result = static_CheckDevice(hRemoteDevice);
+ if (result != NFCSTATUS_SUCCESS)
+ {
+ return result;
+ }
+
/* Send data to the logical link */
result = phFriNfc_LlcpTransport_SendTo( psSocket,
nSap,