/* * FreeRTOS-Cellular-Interface v1.3.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS */ #include "cellular_config.h" #include "cellular_config_defaults.h" /* Standard includes. */ #include <stdio.h> #include <string.h> #include <stdlib.h> /* Cellular includes. */ #include "cellular_platform.h" #include "cellular_types.h" #include "cellular_api.h" #include "cellular_common_api.h" #include "cellular_common.h" #include "cellular_at_core.h" /* Cellular module incliudes. */ #include "cellular_r4.h" /*-----------------------------------------------------------*/ /* TODO : confirm the value. */ #define PDN_ACT_PACKET_REQ_TIMEOUT_MS ( 150000UL ) #define PDN_DEACT_PACKET_REQ_TIMEOUT_MS ( 40000UL ) #define GPRS_ATTACH_REQ_TIMEOUT_MS ( 180000UL ) #define DNS_QUERY_REQ_TIMEOUT_MS ( 120000UL ) #define SOCKET_CLOSE_PACKET_REQ_TIMEOUT_MS ( 120000U ) #define SOCKET_CONNECT_PACKET_REQ_TIMEOUT_MS ( 120000U ) #define CELLULAR_AT_CMD_TYPICAL_MAX_SIZE ( 32U ) #define DATA_SEND_TIMEOUT_MS ( 10000U ) #define PACKET_REQ_TIMEOUT_MS ( 10000U ) #define DATA_READ_TIMEOUT_MS ( 50000UL ) #define SOCKET_DATA_PREFIX_TOKEN "+USORD: " #define SOCKET_DATA_PREFIX_TOKEN_LEN ( 8U ) #define SOCKET_DATA_PREFIX_STRING_LENGTH ( SOCKET_DATA_PREFIX_TOKEN_LEN + 9U ) #define RAT_PRIOIRTY_LIST_LENGTH ( 3U ) /** * @brief Parameters involved in receiving data through sockets */ typedef struct _socketDataRecv { uint32_t * pDataLen; uint8_t * pData; } _socketDataRecv_t; /*-----------------------------------------------------------*/ static CellularPktStatus_t socketRecvDataPrefix( void * pCallbackContext, char * pLine, uint32_t lineLength, char ** ppDataStart, uint32_t * pDataLength ); static CellularATError_t getDataFromResp( const CellularATCommandResponse_t * pAtResp, const _socketDataRecv_t * pDataRecv, uint32_t outBufSize ); static CellularPktStatus_t _Cellular_RecvFuncData( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ); static CellularError_t storeAccessModeAndAddress( CellularContext_t * pContext, CellularSocketHandle_t socketHandle, CellularSocketAccessMode_t dataAccessMode, const CellularSocketAddress_t * pRemoteSocketAddress ); static CellularPktStatus_t _Cellular_RecvFuncGetSocketId( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ); static CellularError_t _Cellular_GetSocketNumber( CellularHandle_t cellularHandle, CellularSocketHandle_t socketHandle, uint8_t * pSessionId ); CellularError_t Cellular_SetPdnConfig( CellularHandle_t cellularHandle, uint8_t contextId, const CellularPdnConfig_t * pPdnConfig ); static CellularError_t _Cellular_GetPacketSwitchStatus( CellularHandle_t cellularHandle, bool * pPacketSwitchStatus ); static CellularError_t _Cellular_GetContextActivationStatus( CellularHandle_t cellularHandle, CellularPdnContextActInfo_t * pPdpContextsActInfo ); static bool _parseExtendedSignalQuality( char * pQcsqPayload, CellularSignalInfo_t * pSignalInfo ); static bool _parseSignalQuality( char * pQcsqPayload, CellularSignalInfo_t * pSignalInfo ); static CellularPktStatus_t _Cellular_RecvFuncGetSignalInfo( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ); static CellularError_t controlSignalStrengthIndication( CellularContext_t * pContext, bool enable ); CellularError_t Cellular_SetPsmSettings( CellularHandle_t cellularHandle, const CellularPsmSettings_t * pPsmSettings ); static CellularError_t _Cellular_isSockOptSupport( CellularSocketOptionLevel_t optionLevel, CellularSocketOption_t option ); /*-----------------------------------------------------------*/ static CellularPktStatus_t socketRecvDataPrefix( void * pCallbackContext, char * pLine, uint32_t lineLength, char ** ppDataStart, uint32_t * pDataLength ) { char * pToken = NULL; int32_t tempValue = 0; CellularATError_t atResult = CELLULAR_AT_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char pLocalLine[ SOCKET_DATA_PREFIX_STRING_LENGTH + 1 ] = { '\0' }; char * pDataStart = pLocalLine; ( void ) lineLength; /* pCallbackContext is not used in this function. It should be NULL. */ if( ( pLine == NULL ) || ( ppDataStart == NULL ) || ( pDataLength == NULL ) || ( pCallbackContext != NULL ) ) { LogError( ( "socketRecvData: Bad parameters" ) ); pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else if( strncmp( pLine, SOCKET_DATA_PREFIX_TOKEN, SOCKET_DATA_PREFIX_TOKEN_LEN ) != 0 ) { /* Prefix string which is not SOCKET_DATA_PREFIX_TOKEN does't indicate data start. * Set data length to 0.*/ *ppDataStart = NULL; *pDataLength = 0; } else { /* The string length of "+USORD: <socket>[,<length>],"<data in text mode>". */ strncpy( pLocalLine, pLine, SOCKET_DATA_PREFIX_STRING_LENGTH ); pDataStart = &pLocalLine[ SOCKET_DATA_PREFIX_TOKEN_LEN ]; /* Skip the socket number. */ atResult = Cellular_ATGetNextTok( &pDataStart, &pToken ); /* Parse the receive data length. */ if( ( atResult == CELLULAR_AT_SUCCESS ) && ( pDataStart[ 0 ] == '"' ) ) { /* Peek the next symbol. If it is \", then length is ommited. Received byte is 0. */ LogDebug( ( "socketRecvData: 0 received bytes." ) ); *pDataLength = 0U; *ppDataStart = NULL; } else if( atResult == CELLULAR_AT_SUCCESS ) { /* Parse the length. */ atResult = Cellular_ATGetNextTok( &pDataStart, &pToken ); if( atResult == CELLULAR_AT_SUCCESS ) { atResult = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( ( tempValue < 0 ) && ( ( uint32_t ) tempValue > CELLULAR_MAX_RECV_DATA_LEN ) ) { LogError( ( "socketRecvData: Bad parameters" ) ); atResult = CELLULAR_AT_ERROR; } } if( atResult == CELLULAR_AT_SUCCESS ) { /* Change +USROD: <socket>,<length>," to +USROD: <socket>,<length>,\r. */ pDataStart = &pLine[ pDataStart - pLocalLine ]; if( pDataStart[ 0 ] == '\"' ) { *pDataStart = '\r'; *ppDataStart = ( char * ) &pDataStart[ 1 ]; *pDataLength = ( uint32_t ) tempValue; } } } else { LogDebug( ( "socketRecvDataPrefix : pLine [%s] with lineLength [%d] is not data prefix", pLine, lineLength ) ); } } return pktStatus; } /*-----------------------------------------------------------*/ static CellularATError_t getDataFromResp( const CellularATCommandResponse_t * pAtResp, const _socketDataRecv_t * pDataRecv, uint32_t outBufSize ) { CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; const char * pInputLine = NULL; uint32_t dataLenToCopy = 0; /* Check if the received data size is greater than the output buffer size. */ if( *pDataRecv->pDataLen > outBufSize ) { LogError( ( "Data is turncated, received data length %u, out buffer size %u", *pDataRecv->pDataLen, outBufSize ) ); dataLenToCopy = outBufSize; *pDataRecv->pDataLen = outBufSize; } else { dataLenToCopy = *pDataRecv->pDataLen; } /* Data is stored in the next intermediate response. */ pInputLine = pAtResp->pItm->pNext->pLine; if( ( pInputLine != NULL ) && ( dataLenToCopy > 0U ) ) { /* Copy the data to the out buffer. */ ( void ) memcpy( ( void * ) pDataRecv->pData, ( const void * ) pInputLine, dataLenToCopy ); } else { LogError( ( "Receive Data: paramerter error, data pointer %p, data to copy %u", pInputLine, dataLenToCopy ) ); atCoreStatus = CELLULAR_AT_BAD_PARAMETER; } return atCoreStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library types. */ /* coverity[misra_c_2012_rule_8_13_violation] */ static CellularPktStatus_t _Cellular_RecvFuncData( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ) { CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char * pInputLine = NULL, * pToken = NULL; const _socketDataRecv_t * pDataRecv = ( _socketDataRecv_t * ) pData; int32_t tempValue = 0; if( pContext == NULL ) { LogError( ( "Receive Data: invalid context" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else if( ( pAtResp == NULL ) || ( pAtResp->pItm == NULL ) || ( pAtResp->pItm->pLine == NULL ) ) { LogError( ( "Receive Data: response is invalid" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else if( ( pDataRecv == NULL ) || ( pDataRecv->pData == NULL ) || ( pDataRecv->pDataLen == NULL ) ) { LogError( ( "Receive Data: Bad parameters" ) ); pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else { pInputLine = pAtResp->pItm->pLine; /* The first item is the data prefix. */ /* Check the data prefix token "+USORD: ". */ if( strncmp( pInputLine, SOCKET_DATA_PREFIX_TOKEN, SOCKET_DATA_PREFIX_TOKEN_LEN ) != 0 ) { LogError( ( "response item error in prefix CONNECT" ) ); atCoreStatus = CELLULAR_AT_ERROR; } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { if( pAtResp->pItm->pNext == NULL ) { /* Modem return +USORD: 0,"". No data returned since there is no data * length field in modem response. */ *pDataRecv->pDataLen = 0; } else { /* Parse the data length. */ atCoreStatus = Cellular_ATGetNextTok( &pInputLine, &pToken ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pInputLine, &pToken ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( tempValue >= 0 ) { *pDataRecv->pDataLen = ( uint32_t ) tempValue; } else { atCoreStatus = CELLULAR_AT_ERROR; } } /* Process the data buffer. Modem may also return +USORD: 0,0,"" with 0 data length. * Process the data response only when data length is greater than 0. */ if( ( atCoreStatus == CELLULAR_AT_SUCCESS ) && ( *pDataRecv->pDataLen > 0U ) ) { atCoreStatus = getDataFromResp( pAtResp, pDataRecv, dataLen ); } } } pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); } return pktStatus; } /*-----------------------------------------------------------*/ static CellularError_t storeAccessModeAndAddress( CellularContext_t * pContext, CellularSocketHandle_t socketHandle, CellularSocketAccessMode_t dataAccessMode, const CellularSocketAddress_t * pRemoteSocketAddress ) { CellularError_t cellularStatus = CELLULAR_SUCCESS; /* pContext is checked in _Cellular_CheckLibraryStatus function. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus != CELLULAR_SUCCESS ) { cellularStatus = CELLULAR_INVALID_HANDLE; } else if( ( pRemoteSocketAddress == NULL ) || ( socketHandle == NULL ) ) { LogDebug( ( "storeAccessModeAndAddress: Invalid socket address" ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else if( socketHandle->socketState != SOCKETSTATE_ALLOCATED ) { LogError( ( "storeAccessModeAndAddress, bad socket state %d", socketHandle->socketState ) ); cellularStatus = CELLULAR_INTERNAL_FAILURE; } else if( dataAccessMode != CELLULAR_ACCESSMODE_BUFFER ) { LogError( ( "storeAccessModeAndAddress, Access mode not supported %d", dataAccessMode ) ); cellularStatus = CELLULAR_UNSUPPORTED; } else { socketHandle->remoteSocketAddress.port = pRemoteSocketAddress->port; socketHandle->dataMode = dataAccessMode; socketHandle->remoteSocketAddress.ipAddress.ipAddressType = pRemoteSocketAddress->ipAddress.ipAddressType; ( void ) strncpy( socketHandle->remoteSocketAddress.ipAddress.ipAddress, pRemoteSocketAddress->ipAddress.ipAddress, CELLULAR_IP_ADDRESS_MAX_SIZE + 1U ); } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library types. */ /* coverity[misra_c_2012_rule_8_13_violation] */ static CellularPktStatus_t _Cellular_RecvFuncGetSocketId( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ) { char * pInputLine = NULL; uint8_t * pSessionId = pData; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; int32_t tempValue = 0; if( pContext == NULL ) { LogError( ( "GetSocketId: Invalid handle" ) ); pktStatus = CELLULAR_PKT_STATUS_INVALID_HANDLE; } else if( ( pSessionId == NULL ) || ( dataLen != sizeof( uint8_t ) ) ) { LogError( ( "GetSocketId: Bad parameters" ) ); pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else if( ( pAtResp == NULL ) || ( pAtResp->pItm == NULL ) || ( pAtResp->pItm->pLine == NULL ) ) { LogError( ( "GetSocketId: Input Line passed is NULL" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else { pInputLine = pAtResp->pItm->pLine; atCoreStatus = Cellular_ATRemovePrefix( &pInputLine ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemoveAllWhiteSpaces( pInputLine ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATStrtoi( pInputLine, 10, &tempValue ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { if( ( tempValue >= MIN_TCP_SESSION_ID ) && ( tempValue <= MAX_TCP_SESSION_ID ) ) { *pSessionId = ( uint8_t ) tempValue; } else { atCoreStatus = CELLULAR_AT_ERROR; } } } pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); } return pktStatus; } /*-----------------------------------------------------------*/ static CellularError_t _Cellular_GetSocketNumber( CellularHandle_t cellularHandle, CellularSocketHandle_t socketHandle, uint8_t * pSessionId ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularAtReq_t atReqSocketConnect = { "AT+USOCR=6,0", CELLULAR_AT_WITH_PREFIX, "+USOCR", _Cellular_RecvFuncGetSocketId, NULL, sizeof( uint8_t ), }; ( void ) socketHandle; atReqSocketConnect.pData = pSessionId; /* Internal function. Caller checks parameters. */ if( cellularStatus == CELLULAR_SUCCESS ) { pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqSocketConnect ); if( pktStatus != CELLULAR_PKT_STATUS_OK ) { LogError( ( "_Cellular_GetSocketNumber: get socekt number failed PktRet: %d", pktStatus ) ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ /* coverity[misra_c_2012_rule_8_13_violation] */ CellularError_t Cellular_SocketRecv( CellularHandle_t cellularHandle, CellularSocketHandle_t socketHandle, /* coverity[misra_c_2012_rule_8_13_violation] */ uint8_t * pBuffer, uint32_t bufferLength, /* coverity[misra_c_2012_rule_8_13_violation] */ uint32_t * pReceivedDataLength ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_TYPICAL_MAX_SIZE ] = { '\0' }; uint32_t recvTimeout = DATA_READ_TIMEOUT_MS; uint32_t recvLen = bufferLength; _socketDataRecv_t dataRecv = { 0 }; CellularAtReq_t atReqSocketRecv = { NULL, CELLULAR_AT_MULTI_DATA_WO_PREFIX, "+USORD", _Cellular_RecvFuncData, NULL, 0, }; uint32_t sessionId = 0; dataRecv.pDataLen = pReceivedDataLength; dataRecv.pData = pBuffer; atReqSocketRecv.pAtCmd = cmdBuf; atReqSocketRecv.pData = ( void * ) &dataRecv; atReqSocketRecv.dataLen = bufferLength; cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus != CELLULAR_SUCCESS ) { LogError( ( "Cellular_SocketRecv: _Cellular_CheckLibraryStatus failed." ) ); } else if( socketHandle == NULL ) { LogError( ( "Cellular_SocketRecv: Invalid socketHandle." ) ); cellularStatus = CELLULAR_INVALID_HANDLE; } else if( ( pBuffer == NULL ) || ( pReceivedDataLength == NULL ) || ( bufferLength == 0U ) ) { LogError( ( "Cellular_SocketRecv: Bad input Param." ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else if( socketHandle->socketState != SOCKETSTATE_CONNECTED ) { /* Check the socket connection state. */ LogInfo( ( "Cellular_SocketRecv: socket state %d is not connected.", socketHandle->socketState ) ); if( ( socketHandle->socketState == SOCKETSTATE_ALLOCATED ) || ( socketHandle->socketState == SOCKETSTATE_CONNECTING ) ) { cellularStatus = CELLULAR_SOCKET_NOT_CONNECTED; } else { cellularStatus = CELLULAR_SOCKET_CLOSED; } } else { sessionId = _Cellular_GetSessionId( pContext, socketHandle->socketId ); if( sessionId == INVALID_SESSION_ID ) { LogError( ( "Cellular_SocketRecv : invalid session ID for socket index %u", socketHandle->socketId ) ); cellularStatus = CELLULAR_INVALID_HANDLE; } } if( cellularStatus == CELLULAR_SUCCESS ) { /* Update recvLen to maximum module length. */ if( CELLULAR_MAX_RECV_DATA_LEN <= bufferLength ) { recvLen = ( uint32_t ) CELLULAR_MAX_RECV_DATA_LEN; } /* Update receive timeout to default timeout if not set with setsocketopt. */ if( recvLen > 0 ) { if( socketHandle->recvTimeoutMs != 0U ) { recvTimeout = socketHandle->recvTimeoutMs; } /* The return value of snprintf is not used. * The max length of the string is fixed and checked offline. */ /* coverity[misra_c_2012_rule_21_6_violation]. */ ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_TYPICAL_MAX_SIZE, "%s%u,%u", "AT+USORD=", sessionId, recvLen ); pktStatus = _Cellular_TimeoutAtcmdDataRecvRequestWithCallback( pContext, atReqSocketRecv, recvTimeout, socketRecvDataPrefix, NULL ); if( pktStatus != CELLULAR_PKT_STATUS_OK ) { /* Reset data handling parameters. */ LogError( ( "Cellular_SocketRecv: Data Receive fail, pktStatus: %d", pktStatus ) ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } } } return cellularStatus; } /*-----------------------------------------------------------*/ static CellularPktStatus_t socketSendDataPrefix( void * pCallbackContext, char * pLine, uint32_t * pBytesRead ) { CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; if( ( pLine == NULL ) || ( pBytesRead == NULL ) ) { LogError( ( "socketSendDataPrefix: pLine is invalid or pBytesRead is invalid" ) ); pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else if( pCallbackContext != NULL ) { LogError( ( "socketSendDataPrefix: pCallbackContext is not NULL" ) ); pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else if( *pBytesRead != 1U ) { LogDebug( ( "socketSendDataPrefix: pBytesRead %u %s is not 1", *pBytesRead, pLine ) ); } else { /* After the data prefix, there should not be any data in stream. * Cellular commmon processes AT command in lines. Add a '\0' after '@'. */ if( strcmp( pLine, "@" ) == 0 ) { pLine[ 1 ] = '\n'; *pBytesRead = 2; } } return pktStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ /* coverity[misra_c_2012_rule_8_13_violation] */ CellularError_t Cellular_SocketSend( CellularHandle_t cellularHandle, CellularSocketHandle_t socketHandle, const uint8_t * pData, uint32_t dataLength, /* coverity[misra_c_2012_rule_8_13_violation] */ uint32_t * pSentDataLength ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; uint32_t sendTimeout = DATA_SEND_TIMEOUT_MS; char cmdBuf[ CELLULAR_AT_CMD_TYPICAL_MAX_SIZE ] = { '\0' }; CellularAtReq_t atReqSocketSend = { 0 }; CellularAtDataReq_t atDataReqSocketSend = { 0 }; uint32_t sessionId = 0; atReqSocketSend.atCmdType = CELLULAR_AT_NO_RESULT; atReqSocketSend.pAtCmd = cmdBuf; atDataReqSocketSend.pData = pData; atDataReqSocketSend.dataLen = dataLength; atDataReqSocketSend.pSentDataLength = pSentDataLength; /* pContext is checked in _Cellular_CheckLibraryStatus function. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus != CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_SocketSend: _Cellular_CheckLibraryStatus failed" ) ); } else if( socketHandle == NULL ) { cellularStatus = CELLULAR_INVALID_HANDLE; } else if( ( pData == NULL ) || ( pSentDataLength == NULL ) || ( dataLength == 0U ) ) { LogDebug( ( "Cellular_SocketSend: Invalid parameter" ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else if( socketHandle->socketState != SOCKETSTATE_CONNECTED ) { /* Check the socket connection state. */ LogInfo( ( "Cellular_SocketSend: socket state %d is not connected.", socketHandle->socketState ) ); if( ( socketHandle->socketState == SOCKETSTATE_ALLOCATED ) || ( socketHandle->socketState == SOCKETSTATE_CONNECTING ) ) { cellularStatus = CELLULAR_SOCKET_NOT_CONNECTED; } else { cellularStatus = CELLULAR_SOCKET_CLOSED; } } else { sessionId = _Cellular_GetSessionId( pContext, socketHandle->socketId ); if( sessionId == INVALID_SESSION_ID ) { LogError( ( "Cellular_SocketSend : invalid session ID for socket index %u", socketHandle->socketId ) ); cellularStatus = CELLULAR_INVALID_HANDLE; } } if( cellularStatus == CELLULAR_SUCCESS ) { /* Send data length check. */ if( dataLength > ( uint32_t ) CELLULAR_MAX_SEND_DATA_LEN ) { atDataReqSocketSend.dataLen = ( uint32_t ) CELLULAR_MAX_SEND_DATA_LEN; } /* Check send timeout. If not set by setsockopt, use default value. */ if( socketHandle->sendTimeoutMs != 0U ) { sendTimeout = socketHandle->sendTimeoutMs; } /* The return value of snprintf is not used. * The max length of the string is fixed and checked offline. */ /* coverity[misra_c_2012_rule_21_6_violation]. */ ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_TYPICAL_MAX_SIZE, "%s%u,%u", "AT+USOWR=", sessionId, atDataReqSocketSend.dataLen ); pktStatus = _Cellular_AtcmdDataSend( pContext, atReqSocketSend, atDataReqSocketSend, socketSendDataPrefix, NULL, PACKET_REQ_TIMEOUT_MS, sendTimeout, 200U ); if( pktStatus != CELLULAR_PKT_STATUS_OK ) { if( socketHandle->socketState == SOCKETSTATE_DISCONNECTED ) { LogInfo( ( "Cellular_SocketSend: Data send fail, socket already closed" ) ); cellularStatus = CELLULAR_SOCKET_CLOSED; } else { LogError( ( "Cellular_SocketSend: Data send fail, PktRet: %d", pktStatus ) ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } } } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_SocketClose( CellularHandle_t cellularHandle, CellularSocketHandle_t socketHandle ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_TYPICAL_MAX_SIZE ] = { '\0' }; CellularAtReq_t atReqSocketClose = { NULL, CELLULAR_AT_NO_RESULT, NULL, NULL, NULL, 0, }; uint32_t sessionId = INVALID_SESSION_ID; cellularModuleContext_t * pModuleContext = NULL; atReqSocketClose.pAtCmd = cmdBuf; /* pContext is checked in _Cellular_CheckLibraryStatus function. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus != CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_SocketClose: _Cellular_CheckLibraryStatus failed" ) ); } else if( socketHandle == NULL ) { cellularStatus = CELLULAR_INVALID_HANDLE; } else { if( socketHandle->socketState == SOCKETSTATE_CONNECTING ) { LogWarn( ( "Cellular_SocketClose: Socket state is SOCKETSTATE_CONNECTING." ) ); } cellularStatus = _Cellular_GetModuleContext( pContext, ( void ** ) &pModuleContext ); } if( cellularStatus == CELLULAR_SUCCESS ) { sessionId = _Cellular_GetSessionId( pContext, socketHandle->socketId ); if( sessionId == INVALID_SESSION_ID ) { LogError( ( "Cellular_SocketClose : invalid session ID for socket index %u", socketHandle->socketId ) ); cellularStatus = CELLULAR_INVALID_HANDLE; } } if( cellularStatus == CELLULAR_SUCCESS ) { /* Remove the mapping. */ pModuleContext->pSessionMap[ sessionId ] = INVALID_SOCKET_INDEX; /* Close the socket. */ if( socketHandle->socketState == SOCKETSTATE_CONNECTED ) { ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_TYPICAL_MAX_SIZE, "%s%u,%d", "AT+USOCL=", sessionId, CELLULAR_CONFIG_SET_SOCKET_CLOSE_ASYNC_MODE ); pktStatus = _Cellular_TimeoutAtcmdRequestWithCallback( pContext, atReqSocketClose, SOCKET_CLOSE_PACKET_REQ_TIMEOUT_MS ); /* Delete the socket config. */ if( pktStatus != CELLULAR_PKT_STATUS_OK ) { LogWarn( ( "Cellular_SocketClose: AT+USOCL fail, PktRet: %d", pktStatus ) ); } } /* Ignore the result from the info, and force to remove the socket. */ cellularStatus = _Cellular_RemoveSocketData( pContext, socketHandle ); } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_SocketConnect( CellularHandle_t cellularHandle, CellularSocketHandle_t socketHandle, CellularSocketAccessMode_t dataAccessMode, const CellularSocketAddress_t * pRemoteSocketAddress ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_MAX_SIZE ] = { '\0' }; uint8_t sessionId = 0; CellularAtReq_t atReqSocketConnect = { NULL, CELLULAR_AT_NO_RESULT, NULL, NULL, NULL, 0, }; cellularModuleContext_t * pModuleContext = NULL; atReqSocketConnect.pAtCmd = cmdBuf; /* Make sure the library is open. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus != CELLULAR_SUCCESS ) { LogError( ( "Cellular_SocketConnect: _Cellular_CheckLibraryStatus failed." ) ); } else if( pRemoteSocketAddress == NULL ) { LogError( ( "Cellular_SocketConnect: Invalid socket address." ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else if( socketHandle == NULL ) { LogError( ( "Cellular_SocketConnect: Invalid socket handle." ) ); cellularStatus = CELLULAR_INVALID_HANDLE; } else if( socketHandle->socketProtocol != CELLULAR_SOCKET_PROTOCOL_TCP ) { LogError( ( "Cellular_SocketConnect: Invalid socket protocol." ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else if( ( socketHandle->socketState == SOCKETSTATE_CONNECTED ) || ( socketHandle->socketState == SOCKETSTATE_CONNECTING ) ) { LogError( ( "Cellular_SocketConnect: Not allowed in state %d.", socketHandle->socketState ) ); cellularStatus = CELLULAR_NOT_ALLOWED; } else { cellularStatus = storeAccessModeAndAddress( pContext, socketHandle, dataAccessMode, pRemoteSocketAddress ); } if( cellularStatus == CELLULAR_SUCCESS ) { cellularStatus = _Cellular_GetModuleContext( pContext, ( void ** ) &pModuleContext ); } /* Set socket config and get session id. The session id is defined by the modem. */ if( cellularStatus == CELLULAR_SUCCESS ) { /* Builds the Socket connect command. */ cellularStatus = _Cellular_GetSocketNumber( pContext, socketHandle, &sessionId ); if( cellularStatus == CELLULAR_SUCCESS ) { /* Create the reverse table to store the socketIndex to sessionId. */ pModuleContext->pSessionMap[ sessionId ] = socketHandle->socketId; } } /* Start the tcp connection. */ if( cellularStatus == CELLULAR_SUCCESS ) { /* The return value of snprintf is not used. * The max length of the string is fixed and checked offline. */ /* coverity[misra_c_2012_rule_21_6_violation]. */ ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_MAX_SIZE, "AT+USOCO=%u,\"%s\",%d,1", sessionId, socketHandle->remoteSocketAddress.ipAddress.ipAddress, socketHandle->remoteSocketAddress.port ); /* Set the socket state to connecting state. If cellular modem returns error, * revert the state to allocated state. */ socketHandle->socketState = SOCKETSTATE_CONNECTING; pktStatus = _Cellular_TimeoutAtcmdRequestWithCallback( pContext, atReqSocketConnect, SOCKET_CONNECT_PACKET_REQ_TIMEOUT_MS ); if( pktStatus != CELLULAR_PKT_STATUS_OK ) { LogError( ( "Cellular_SocketConnect: Socket connect failed, cmdBuf:%s, PktRet: %d", cmdBuf, pktStatus ) ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); /* Revert the state to allocated state. */ socketHandle->socketState = SOCKETSTATE_ALLOCATED; } } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_GetSimCardStatus( CellularHandle_t cellularHandle, CellularSimCardStatus_t * pSimCardStatus ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; /* pContext is checked in _Cellular_CheckLibraryStatus function. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus != CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_GetSimCardStatus: _Cellular_CheckLibraryStatus failed" ) ); } else if( pSimCardStatus == NULL ) { LogWarn( ( "Cellular_GetSimCardStatus: Bad input Parameter " ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else { /* Parameters are checked in this API. */ pSimCardStatus->simCardState = CELLULAR_SIM_CARD_UNKNOWN; pSimCardStatus->simCardLockState = CELLULAR_SIM_CARD_LOCK_UNKNOWN; cellularStatus = Cellular_CommonGetSimCardLockStatus( cellularHandle, pSimCardStatus ); if( cellularStatus == CELLULAR_SUCCESS ) { if( ( pSimCardStatus->simCardLockState != CELLULAR_SIM_CARD_INVALID ) && ( pSimCardStatus->simCardLockState != CELLULAR_SIM_CARD_LOCK_UNKNOWN ) ) { pSimCardStatus->simCardState = CELLULAR_SIM_CARD_INSERTED; } else { pSimCardStatus->simCardState = CELLULAR_SIM_CARD_UNKNOWN; } LogInfo( ( "Cellular_GetSimCardStatus, Sim Insert State[%d], Lock State[%d]", pSimCardStatus->simCardState, pSimCardStatus->simCardLockState ) ); } } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_DeactivatePdn( CellularHandle_t cellularHandle, uint8_t contextId ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_MAX_SIZE ] = { '\0' }; bool packetSwitchStatus = false; uint32_t i = 0; CellularServiceStatus_t serviceStatus = { 0 }; CellularPdnContextActInfo_t pdpContextsActInfo = { 0 }; CellularAtReq_t atReqDeactPdn = { NULL, CELLULAR_AT_NO_RESULT, NULL, NULL, NULL, 0, }; atReqDeactPdn.pAtCmd = cmdBuf; cellularStatus = _Cellular_IsValidPdn( contextId ); if( cellularStatus == CELLULAR_SUCCESS ) { /* Make sure the library is open. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); } if( cellularStatus == CELLULAR_SUCCESS ) { /* Get current network operator settings. */ cellularStatus = Cellular_CommonGetServiceStatus( cellularHandle, &serviceStatus ); if( cellularStatus == CELLULAR_SUCCESS ) { /* Check the current <Act> status of context. */ cellularStatus = _Cellular_GetContextActivationStatus( cellularHandle, &pdpContextsActInfo ); } if( cellularStatus == CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_DeactivatePdn: Listing operator and context details below." ) ); for( i = 0U; i < ( MAX_PDP_CONTEXTS - 1 ); i++ ) { /* Print only those contexts that are present in +CGACT response */ if( pdpContextsActInfo.contextsPresent[ i ] ) { LogDebug( ( "Context [%d], Act State [%d], Operator <Act> [%d]\r\n", i + 1, pdpContextsActInfo.contextActState[ i ], serviceStatus.rat ) ); } } /* Deactivate context if active */ if( pdpContextsActInfo.contextActState[ contextId - 1 ] == true ) { /* Don't deactivate LTE default bearer context */ /* Otherwise sending AT command "+CGACT=0,1" for deactivation will result in ERROR */ if( ( serviceStatus.rat >= CELLULAR_RAT_LTE ) && ( contextId == DEFAULT_BEARER_CONTEXT_ID ) ) { LogInfo( ( "Cellular_DeactivatePdn: Default Bearer context %d Active. Not allowed to deactivate.", contextId ) ); cellularStatus = CELLULAR_NOT_ALLOWED; } else { ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_MAX_SIZE, "%s=0,%u", "AT+CGACT", contextId ); pktStatus = _Cellular_TimeoutAtcmdRequestWithCallback( pContext, atReqDeactPdn, PDN_DEACT_PACKET_REQ_TIMEOUT_MS ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } if( ( cellularStatus != CELLULAR_SUCCESS ) && ( cellularStatus != CELLULAR_NOT_ALLOWED ) ) { LogError( ( "Cellular_DeactivatePdn: can't deactivate PDN, PktRet: %d", pktStatus ) ); /* Sometimes +CGACT deactivation fails in 2G. Then check packet switch attach. If attached, detach packet switch. */ if( ( serviceStatus.rat == CELLULAR_RAT_GSM ) || ( serviceStatus.rat == CELLULAR_RAT_EDGE ) ) { cellularStatus = _Cellular_GetPacketSwitchStatus( cellularHandle, &packetSwitchStatus ); if( ( cellularStatus == CELLULAR_SUCCESS ) && ( packetSwitchStatus == true ) ) { LogError( ( "Deactivate Packet switch" ) ); ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_MAX_SIZE, "%s", "AT+CGATT=0" ); pktStatus = _Cellular_TimeoutAtcmdRequestWithCallback( pContext, atReqDeactPdn, GPRS_ATTACH_REQ_TIMEOUT_MS ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } else if( cellularStatus != CELLULAR_SUCCESS ) { LogError( ( "Packet switch query failed" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } else { LogInfo( ( "Packet switch detached" ) ); } } } } else { LogInfo( ( "Cellular_DeactivatePdn: Context id [%d] is already deactive", contextId ) ); } } else { LogError( ( "Cellular_DeactivatePdn: Unable to list operator and context details." ) ); } } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library prototype. */ /* coverity[misra_c_2012_rule_8_13_violation] */ static CellularPktStatus_t _Cellular_RecvFuncPacketSwitchStatus( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ) { char * pInputLine = NULL; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; bool * pPacketSwitchStatus = ( bool * ) pData; if( pContext == NULL ) { LogError( ( "PacketSwitchStatus: Invalid handle" ) ); pktStatus = CELLULAR_PKT_STATUS_INVALID_HANDLE; } else if( ( pData == NULL ) || ( dataLen != sizeof( bool ) ) ) { LogError( ( "GetPacketSwitchStatus: Invalid param" ) ); pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else if( ( pAtResp == NULL ) || ( pAtResp->pItm == NULL ) || ( pAtResp->pItm->pLine == NULL ) ) { LogError( ( "GetPacketSwitchStatus: Input Line passed is NULL" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else { pInputLine = pAtResp->pItm->pLine; /* Remove prefix. */ atCoreStatus = Cellular_ATRemovePrefix( &pInputLine ); /* Remove leading space. */ if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemoveLeadingWhiteSpaces( &pInputLine ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { if( *pInputLine == '0' ) { *pPacketSwitchStatus = false; } else if( *pInputLine == '1' ) { *pPacketSwitchStatus = true; } else { atCoreStatus = CELLULAR_AT_ERROR; } } pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); } return pktStatus; } /*-----------------------------------------------------------*/ /* Get PDN context Activation state */ static CellularPktStatus_t _Cellular_RecvFuncGetPdpContextActState( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ) { char * pRespLine = NULL; CellularPdnContextActInfo_t * pPDPContextsActInfo = ( CellularPdnContextActInfo_t * ) pData; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; const CellularATCommandLine_t * pCommnadItem = NULL; uint8_t tokenIndex = 0; uint8_t contextId = 0; int32_t tempValue = 0; char * pToken = NULL; if( pContext == NULL ) { LogError( ( "_Cellular_RecvFuncGetPdpContextActState: invalid context" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else if( ( pPDPContextsActInfo == NULL ) || ( dataLen != sizeof( CellularPdnContextActInfo_t ) ) ) { pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else if( pAtResp == NULL ) { LogError( ( "_Cellular_RecvFuncGetPdpContextActState: Response is invalid" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else if( ( pAtResp->pItm == NULL ) || ( pAtResp->pItm->pLine == NULL ) ) { LogError( ( "_Cellular_RecvFuncGetPdpContextActState: no PDN context available" ) ); pktStatus = CELLULAR_PKT_STATUS_OK; } else { pRespLine = pAtResp->pItm->pLine; pCommnadItem = pAtResp->pItm; while( pCommnadItem != NULL ) { pRespLine = pCommnadItem->pLine; LogDebug( ( "_Cellular_RecvFuncGetPdpContextActState: pRespLine [%s]", pRespLine ) ); /* Removing all the Spaces in the AT Response. */ atCoreStatus = Cellular_ATRemoveAllWhiteSpaces( pRespLine ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemovePrefix( &pRespLine ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemoveAllDoubleQuote( pRespLine ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pRespLine, &pToken ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { tokenIndex = 0; while( ( pToken != NULL ) && ( atCoreStatus == CELLULAR_AT_SUCCESS ) ) { switch( tokenIndex ) { case ( CELLULAR_PDN_ACT_STATUS_POS_CONTEXT_ID ): LogDebug( ( "_Cellular_RecvFuncGetPdpContextActState: Context Id pToken: %s", pToken ) ); atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { if( ( tempValue >= ( int32_t ) CELLULAR_PDN_CONTEXT_ID_MIN ) && ( tempValue <= ( int32_t ) MAX_PDP_CONTEXTS ) ) { contextId = ( uint8_t ) tempValue; pPDPContextsActInfo->contextsPresent[ contextId - 1 ] = true; LogDebug( ( "_Cellular_RecvFuncGetPdpContextActState: Context Id: %d", contextId ) ); } else { LogError( ( "_Cellular_RecvFuncGetPdpContextActState: Invalid Context Id. Token %s", pToken ) ); atCoreStatus = CELLULAR_AT_ERROR; } } break; case ( CELLULAR_PDN_ACT_STATUS_POS_CONTEXT_STATE ): LogDebug( ( "_Cellular_RecvFuncGetPdpContextActState: Context <Act> pToken: %s", pToken ) ); atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { pPDPContextsActInfo->contextActState[ contextId - 1 ] = ( bool ) tempValue; LogDebug( ( "_Cellular_RecvFuncGetPdpContextActState: Context <Act>: %d", pPDPContextsActInfo->contextActState[ contextId - 1 ] ) ); } break; default: break; } tokenIndex++; if( Cellular_ATGetNextTok( &pRespLine, &pToken ) != CELLULAR_AT_SUCCESS ) { break; } } } } pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); if( pktStatus != CELLULAR_PKT_STATUS_OK ) { LogError( ( "_Cellular_RecvFuncGetPdpContextActState: parse %s failed", pRespLine ) ); break; } pCommnadItem = pCommnadItem->pNext; } } return pktStatus; } /*-----------------------------------------------------------*/ /* Check activation status of particular context. */ static CellularError_t _Cellular_GetContextActivationStatus( CellularHandle_t cellularHandle, CellularPdnContextActInfo_t * pPdpContextsActInfo ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularAtReq_t atReqPacketSwitchStatus = { "AT+CGACT?", CELLULAR_AT_MULTI_WITH_PREFIX, "+CGACT", _Cellular_RecvFuncGetPdpContextActState, NULL, sizeof( CellularPdnContextActInfo_t ), }; atReqPacketSwitchStatus.pData = pPdpContextsActInfo; /* Internal function. Callee check parameters. */ pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqPacketSwitchStatus ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); return cellularStatus; } /*-----------------------------------------------------------*/ static CellularError_t _Cellular_GetPacketSwitchStatus( CellularHandle_t cellularHandle, bool * pPacketSwitchStatus ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularAtReq_t atReqPacketSwitchStatus = { "AT+CGATT?", CELLULAR_AT_WITH_PREFIX, "+CGATT", _Cellular_RecvFuncPacketSwitchStatus, NULL, sizeof( bool ), }; atReqPacketSwitchStatus.pData = pPacketSwitchStatus; /* Internal function. Callee check parameters. */ pktStatus = _Cellular_TimeoutAtcmdRequestWithCallback( pContext, atReqPacketSwitchStatus, PDN_ACT_PACKET_REQ_TIMEOUT_MS ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_ActivatePdn( CellularHandle_t cellularHandle, uint8_t contextId ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_MAX_SIZE ] = { '\0' }; uint32_t i = 0; CellularPdnContextActInfo_t pdpContextsActInfo = { 0 }; CellularAtReq_t atReqActPdn = { NULL, CELLULAR_AT_NO_RESULT, NULL, NULL, NULL, 0, }; cellularStatus = _Cellular_IsValidPdn( contextId ); atReqActPdn.pAtCmd = cmdBuf; if( cellularStatus == CELLULAR_SUCCESS ) { /* Make sure the library is open. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); } if( cellularStatus == CELLULAR_SUCCESS ) { /* Check the current <Act> status of context. If not activated, activate the PDN context ID. */ cellularStatus = _Cellular_GetContextActivationStatus( cellularHandle, &pdpContextsActInfo ); if( cellularStatus == CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_ActivatePdn: Listing operator and context details below." ) ); for( i = 0U; i < ( MAX_PDP_CONTEXTS - 1 ); i++ ) { /* Print only those contexts that are present in +CGACT response */ if( pdpContextsActInfo.contextsPresent[ i ] ) { LogDebug( ( "Cellular_ActivatePdn: Context [%d], Act State [%d]\r\n", i + 1, pdpContextsActInfo.contextActState[ i ] ) ); } } } /* Activate context if not already active */ if( pdpContextsActInfo.contextActState[ contextId - 1 ] == false ) { if( pktStatus == CELLULAR_PKT_STATUS_OK ) { ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_MAX_SIZE, "%s=1,%u", "AT+CGACT", contextId ); pktStatus = _Cellular_TimeoutAtcmdRequestWithCallback( pContext, atReqActPdn, PDN_ACT_PACKET_REQ_TIMEOUT_MS ); } if( pktStatus != CELLULAR_PKT_STATUS_OK ) { LogError( ( "Cellular_ActivatePdn: can't activate PDN, PktRet: %d", pktStatus ) ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } } else { LogInfo( ( "Cellular_ActivatePdn: Context id [%d] is already active", contextId ) ); } } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ /* coverity[misra_c_2012_rule_8_13_violation] */ CellularError_t Cellular_GetPdnStatus( CellularHandle_t cellularHandle, CellularPdnStatus_t * pPdnStatusBuffers, uint8_t numStatusBuffers, uint8_t * pNumStatus ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; uint8_t i = 0; CellularPdnContextActInfo_t pdpContextsActInfo = { 0 }; if( cellularStatus == CELLULAR_SUCCESS ) { /* Make sure the library is open. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); } if( ( pPdnStatusBuffers == NULL ) || ( pNumStatus == NULL ) || ( numStatusBuffers < 1u ) ) { cellularStatus = CELLULAR_BAD_PARAMETER; LogWarn( ( "_Cellular_GetPdnStatus: Bad input Parameter " ) ); } if( cellularStatus == CELLULAR_SUCCESS ) { *pNumStatus = 0U; /* Check the current <Act> status of contexts. */ cellularStatus = _Cellular_GetContextActivationStatus( cellularHandle, &pdpContextsActInfo ); if( cellularStatus == CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_GetPdnStatus: Listing operator and context details below." ) ); for( i = 0U; i < ( MAX_PDP_CONTEXTS - 1 ); i++ ) { /* Print only those contexts that are present in +CGACT response. */ if( pdpContextsActInfo.contextsPresent[ i ] ) { LogDebug( ( "Context [%d], Act State [%d]\r\n", i + 1, pdpContextsActInfo.contextActState[ i ] ) ); if( *pNumStatus < numStatusBuffers ) { pPdnStatusBuffers[ *pNumStatus ].contextId = i + 1U; pPdnStatusBuffers[ *pNumStatus ].state = pdpContextsActInfo.contextActState[ i ]; *pNumStatus = *pNumStatus + 1U; } } } } } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_SetRatPriority( CellularHandle_t cellularHandle, const CellularRat_t * pRatPriorities, uint8_t ratPrioritiesLength ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; uint8_t i = 0; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_MAX_SIZE ] = { '\0' }; CellularAtReq_t atReqSetRatPriority = { NULL, CELLULAR_AT_NO_RESULT, NULL, NULL, NULL, 0, }; cellularStatus = _Cellular_CheckLibraryStatus( pContext ); atReqSetRatPriority.pAtCmd = cmdBuf; if( cellularStatus != CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_SetRatPriority: _Cellular_CheckLibraryStatus failed" ) ); } else if( ( pRatPriorities == NULL ) || ( ratPrioritiesLength == 0U ) || ( ratPrioritiesLength > ( uint8_t ) CELLULAR_MAX_RAT_PRIORITY_COUNT ) ) { LogWarn( ( "Cellular_SetRatPriority: Bad input Parameter " ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else { /* In case of +UMNOPROF=0, AT+URAT set commad is not allowed */ if( CELLULAR_CONFIG_SARA_R4_SET_MNO_PROFILE != 0 ) { ( void ) strcpy( cmdBuf, "AT+CFUN=4;+URAT=" ); while( i < ratPrioritiesLength ) { if( ( pRatPriorities[ i ] == CELLULAR_RAT_GSM ) || ( pRatPriorities[ i ] == CELLULAR_RAT_EDGE ) ) { ( void ) strcat( cmdBuf, "9" ); } else if( pRatPriorities[ i ] == CELLULAR_RAT_CATM1 ) { ( void ) strcat( cmdBuf, "7" ); } else if( pRatPriorities[ i ] == CELLULAR_RAT_NBIOT ) { ( void ) strcat( cmdBuf, "8" ); } else { cellularStatus = CELLULAR_BAD_PARAMETER; break; } i++; if( i < ratPrioritiesLength ) { ( void ) strcat( cmdBuf, "," ); } } ( void ) strcat( cmdBuf, ";+CFUN=1" ); if( cellularStatus == CELLULAR_SUCCESS ) { pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqSetRatPriority ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } } else { LogDebug( ( "Cellular_SetRatPriority: Automatic selection as UMNOPROF profile is 0" ) ); } } return cellularStatus; } /*-----------------------------------------------------------*/ /* Get modem RAT priority setting. */ /* coverity[misra_c_2012_rule_8_13_violation] */ static CellularPktStatus_t _Cellular_RecvFuncGetRatPriority( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ) { char * pInputLine = NULL, * pToken = NULL; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; CellularRat_t * pRatPriorities = NULL; uint8_t ratIndex = 0; uint32_t maxRatPriorityLength = ( dataLen > RAT_PRIOIRTY_LIST_LENGTH ? RAT_PRIOIRTY_LIST_LENGTH : dataLen ); if( pContext == NULL ) { LogError( ( "_Cellular_RecvFuncGetRatPriority: Invalid context" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else if( ( pAtResp == NULL ) || ( pAtResp->pItm == NULL ) || ( pAtResp->pItm->pLine == NULL ) || ( pData == NULL ) || ( dataLen == 0U ) ) { LogError( ( "_Cellular_RecvFuncGetRatPriority: Invalid param" ) ); pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else { pInputLine = pAtResp->pItm->pLine; pRatPriorities = ( CellularRat_t * ) pData; atCoreStatus = Cellular_ATRemovePrefix( &pInputLine ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemoveAllWhiteSpaces( pInputLine ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { memset( pRatPriorities, CELLULAR_RAT_INVALID, dataLen ); /* pInputLine : 7,8,9. */ atCoreStatus = Cellular_ATGetNextTok( &pInputLine, &pToken ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { while( pToken != NULL && ratIndex < maxRatPriorityLength ) { LogDebug( ( "_Cellular_RecvFuncGetRatPriority: pToken [%s]", pToken ) ); if( strcmp( pToken, "7" ) == 0 ) { pRatPriorities[ ratIndex ] = CELLULAR_RAT_CATM1; LogDebug( ( "_Cellular_RecvFuncGetRatPriority: CELLULAR_RAT_CATM1" ) ); } else if( strcmp( pToken, "8" ) == 0 ) { pRatPriorities[ ratIndex ] = CELLULAR_RAT_NBIOT; LogDebug( ( "_Cellular_RecvFuncGetRatPriority: CELLULAR_RAT_NBIOT" ) ); } else if( strcmp( pToken, "9" ) == 0 ) { pRatPriorities[ ratIndex ] = CELLULAR_RAT_GSM; /* or CELLULAR_RAT_EDGE */ LogDebug( ( "_Cellular_RecvFuncGetRatPriority: CELLULAR_RAT_GSM" ) ); } else { LogDebug( ( "_Cellular_RecvFuncGetRatPriority: Invalid RAT string [%s]", pToken ) ); } ratIndex++; if( Cellular_ATGetNextTok( &pInputLine, &pToken ) != CELLULAR_AT_SUCCESS ) { break; } } } } pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); } return pktStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_GetRatPriority( CellularHandle_t cellularHandle, CellularRat_t * pRatPriorities, uint8_t ratPrioritiesLength, uint8_t * pReceiveRatPrioritesLength ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; uint8_t ratIndex = 0; CellularAtReq_t atReqSetRatPriority = { "AT+URAT?", CELLULAR_AT_WITH_PREFIX, "+URAT", _Cellular_RecvFuncGetRatPriority, NULL, 0U, }; atReqSetRatPriority.pData = pRatPriorities; atReqSetRatPriority.dataLen = ( uint16_t ) ratPrioritiesLength; cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus != CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_GetRatPriority: _Cellular_CheckLibraryStatus failed" ) ); } else if( ( pRatPriorities == NULL ) || ( ratPrioritiesLength == 0U ) || ( ratPrioritiesLength > ( uint8_t ) CELLULAR_MAX_RAT_PRIORITY_COUNT ) || ( pReceiveRatPrioritesLength == NULL ) ) { LogWarn( ( "Cellular_GetRatPriority: Bad input Parameter " ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else { /* In case of +UMNOPROF=0, AT+URAT? read commad is not allowed */ if( CELLULAR_CONFIG_SARA_R4_SET_MNO_PROFILE != 0 ) { pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqSetRatPriority ); if( pktStatus == CELLULAR_PKT_STATUS_OK ) { for( ratIndex = 0; ratIndex < ratPrioritiesLength; ratIndex++ ) { if( pRatPriorities[ ratIndex ] == CELLULAR_RAT_INVALID ) { break; } } *pReceiveRatPrioritesLength = ratIndex; } cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } else { LogDebug( ( "Cellular_GetRatPriority: Automatic selection as UMNOPROF profile is 0" ) ); pRatPriorities[ ratIndex++ ] = CELLULAR_RAT_CATM1; pRatPriorities[ ratIndex++ ] = CELLULAR_RAT_NBIOT; pRatPriorities[ ratIndex++ ] = CELLULAR_RAT_GSM; *pReceiveRatPrioritesLength = RAT_PRIOIRTY_LIST_LENGTH; } } return cellularStatus; } /*-----------------------------------------------------------*/ static bool _parseExtendedSignalQuality( char * pQcsqPayload, CellularSignalInfo_t * pSignalInfo ) { char * pToken = NULL, * pTmpQcsqPayload = pQcsqPayload; int32_t tempValue = 0; bool parseStatus = true; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; if( ( pSignalInfo == NULL ) || ( pQcsqPayload == NULL ) ) { LogError( ( "_parseExtendedSignalQuality: Invalid Input Parameters" ) ); parseStatus = false; } /* +CESQ: <rxlev>,<ber>,<rscp>,<ecn0>,<rsrq>,<rsrp>. */ /* Skip <rxlev>. */ atCoreStatus = Cellular_ATGetNextTok( &pTmpQcsqPayload, &pToken ); /* Parse <ber>. */ if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pTmpQcsqPayload, &pToken ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( ( atCoreStatus == CELLULAR_AT_SUCCESS ) && ( tempValue <= INT16_MAX ) && ( tempValue >= INT16_MIN ) ) { /* * Bit Error Rate (BER): * 0..7: as RXQUAL values in the table in 3GPP TS 45.008 [124], subclause 8.2.4 * 99: not known or not detectable */ if( ( tempValue >= 0 ) && ( tempValue <= 7 ) ) { pSignalInfo->ber = ( int16_t ) tempValue; } else { pSignalInfo->ber = CELLULAR_INVALID_SIGNAL_VALUE; } } else { LogError( ( "_parseExtendedSignalQuality: Error in processing BER. Token %s", pToken ) ); atCoreStatus = CELLULAR_AT_ERROR; } } /* Skip <rscp>. */ if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pTmpQcsqPayload, &pToken ); } /* Skip <ecno>. */ if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pTmpQcsqPayload, &pToken ); } /* Parse <rsrq>. */ if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pTmpQcsqPayload, &pToken ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { /* * Reference Signal Received Quality (RSRQ): * 0: less than -19.5 dB * 1..33: from -19.5 dB to -3.5 dB with 0.5 dB steps * 34: -3 dB or greater * 255: not known or not detectable */ if( ( tempValue >= 0 ) && ( tempValue <= 34 ) ) { pSignalInfo->rsrq = ( int16_t ) ( ( -20 ) + ( tempValue * 0.5 ) ); } else { pSignalInfo->rsrq = CELLULAR_INVALID_SIGNAL_VALUE; } } else { LogError( ( "_parseExtendedSignalQuality: Error in processing RSRP. Token %s", pToken ) ); parseStatus = false; } } /* Parse <rsrp>. */ if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pTmpQcsqPayload, &pToken ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { /* * Reference Signal Received Power(RSRP) : * 0 : less than - 140 dBm * 1..96 : from - 140 dBm to - 45 dBm with 1 dBm steps * 97 : -44 dBm or greater * 255 : not known or not detectable */ if( ( tempValue >= 0 ) && ( tempValue <= 97 ) ) { pSignalInfo->rsrp = ( int16_t ) ( ( -141 ) + ( tempValue ) ); } else { pSignalInfo->rsrp = CELLULAR_INVALID_SIGNAL_VALUE; } } else { LogError( ( "_parseExtendedSignalQuality: Error in processing RSRP. Token %s", pToken ) ); parseStatus = false; } } return parseStatus; } /*-----------------------------------------------------------*/ static bool _parseSignalQuality( char * pQcsqPayload, CellularSignalInfo_t * pSignalInfo ) { char * pToken = NULL, * pTmpQcsqPayload = pQcsqPayload; int32_t tempValue = 0; bool parseStatus = true; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; if( ( pSignalInfo == NULL ) || ( pQcsqPayload == NULL ) ) { LogError( ( "_parseSignalQuality: Invalid Input Parameters" ) ); parseStatus = false; } /* +CSQ: <signal_power>,<qual>. */ /* Parse <signal_power>. */ atCoreStatus = Cellular_ATGetNextTok( &pTmpQcsqPayload, &pToken ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { /* * The allowed range is 0-31 and 99. * 0 RSSI of the network <= -113 dBm * 1 -111 dBm * 2...30 -109 dBm <= RSSI of the network <= -53 dBm * 31 -51 dBm <= RSSI of the network * 99 Not known or not detectable */ if( ( tempValue >= 0 ) && ( tempValue <= 31 ) ) { pSignalInfo->rssi = ( int16_t ) ( ( -113 ) + ( tempValue * 2 ) ); } else if( tempValue == 99 ) { pSignalInfo->rssi = -113; } else { pSignalInfo->rssi = CELLULAR_INVALID_SIGNAL_VALUE; } } else { LogError( ( "_parseSignalQuality: Error in processing RSSI. Token %s", pToken ) ); parseStatus = false; } } /* Parse <qual>. */ if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pTmpQcsqPayload, &pToken ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { /* * The allowed range is 0-7 and 99 : * In 2G RAT CS dedicated and GPRS packet transfer mode indicates the Bit Error Rate (BER) as specified in 3GPP TS 45.008 */ if( ( tempValue >= 0 ) && ( tempValue <= 7 ) ) { pSignalInfo->ber = ( int16_t ) tempValue; } else { pSignalInfo->ber = CELLULAR_INVALID_SIGNAL_VALUE; } } else { LogError( ( "_parseSignalQuality: Error in processing ber. Token %s", pToken ) ); parseStatus = false; } } return parseStatus; } /*-----------------------------------------------------------*/ /* parse signal strength response */ static CellularPktStatus_t _Cellular_RecvFuncGetSignalInfo( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ) { char * pInputLine = NULL; CellularSignalInfo_t * pSignalInfo = ( CellularSignalInfo_t * ) pData; bool parseStatus = true; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; bool isExtendedResponse = false; if( pContext == NULL ) { pktStatus = CELLULAR_PKT_STATUS_INVALID_HANDLE; } else if( ( pSignalInfo == NULL ) || ( dataLen != sizeof( CellularSignalInfo_t ) ) ) { pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else if( ( pAtResp == NULL ) || ( pAtResp->pItm == NULL ) || ( pAtResp->pItm->pLine == NULL ) ) { LogError( ( "GetSignalInfo: Input Line passed is NULL" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else { pInputLine = pAtResp->pItm->pLine; if( strstr( pInputLine, "+CESQ" ) ) { LogDebug( ( "GetSignalInfo: ExtendedResponse received." ) ); isExtendedResponse = true; } atCoreStatus = Cellular_ATRemovePrefix( &pInputLine ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemoveAllWhiteSpaces( pInputLine ); } if( atCoreStatus != CELLULAR_AT_SUCCESS ) { pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); } } if( pktStatus == CELLULAR_PKT_STATUS_OK ) { if( isExtendedResponse ) { parseStatus = _parseExtendedSignalQuality( pInputLine, pSignalInfo ); } else { parseStatus = _parseSignalQuality( pInputLine, pSignalInfo ); } if( parseStatus != true ) { pSignalInfo->rssi = CELLULAR_INVALID_SIGNAL_VALUE; pSignalInfo->rsrp = CELLULAR_INVALID_SIGNAL_VALUE; pSignalInfo->rsrq = CELLULAR_INVALID_SIGNAL_VALUE; pSignalInfo->ber = CELLULAR_INVALID_SIGNAL_VALUE; pSignalInfo->bars = CELLULAR_INVALID_SIGNAL_BAR_VALUE; pktStatus = CELLULAR_PKT_STATUS_FAILURE; } } return pktStatus; } /*-----------------------------------------------------------*/ CellularError_t Cellular_GetSignalInfo( CellularHandle_t cellularHandle, CellularSignalInfo_t * pSignalInfo ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularRat_t rat = CELLULAR_RAT_INVALID; CellularAtReq_t atReqQuerySignalInfo = { "AT+CSQ", CELLULAR_AT_WITH_PREFIX, "+CSQ", _Cellular_RecvFuncGetSignalInfo, NULL, sizeof( CellularSignalInfo_t ), }; CellularAtReq_t atReqQueryExtendedSignalInfo = { "AT+CESQ", CELLULAR_AT_WITH_PREFIX, "+CESQ", _Cellular_RecvFuncGetSignalInfo, NULL, sizeof( CellularSignalInfo_t ), }; cellularStatus = _Cellular_CheckLibraryStatus( pContext ); atReqQuerySignalInfo.pData = pSignalInfo; atReqQueryExtendedSignalInfo.pData = pSignalInfo; if( cellularStatus != CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_GetSignalInfo: _Cellular_CheckLibraryStatus failed" ) ); } else if( pSignalInfo == NULL ) { cellularStatus = CELLULAR_BAD_PARAMETER; } else { cellularStatus = _Cellular_GetCurrentRat( pContext, &rat ); } if( cellularStatus == CELLULAR_SUCCESS ) { /* Get +CSQ response */ pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqQuerySignalInfo ); if( pktStatus == CELLULAR_PKT_STATUS_OK ) { /* Get +CESQ response */ pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqQueryExtendedSignalInfo ); if( pktStatus == CELLULAR_PKT_STATUS_OK ) { /* If the convert failed, the API will return CELLULAR_INVALID_SIGNAL_BAR_VALUE in bars field. */ ( void ) _Cellular_ComputeSignalBars( rat, pSignalInfo ); } cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_SetDns( CellularHandle_t cellularHandle, uint8_t contextId, const char * pDnsServerAddress ) { /* Modem use dynamic DNS addresses. Return unsupported. */ ( void ) cellularHandle; ( void ) contextId; ( void ) pDnsServerAddress; return CELLULAR_UNSUPPORTED; } /*-----------------------------------------------------------*/ static CellularError_t controlSignalStrengthIndication( CellularContext_t * pContext, bool enable ) { CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_TYPICAL_MAX_SIZE ] = { '\0' }; uint8_t enable_value = 0; CellularAtReq_t atReqControlSignalStrengthIndication = { NULL, CELLULAR_AT_NO_RESULT, NULL, NULL, NULL, 0, }; atReqControlSignalStrengthIndication.pAtCmd = cmdBuf; if( enable == true ) { enable_value = 1; } else { enable_value = 0; } cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus == CELLULAR_SUCCESS ) { if( enable_value ) { /* Enable signal level change indication via +CIEV URC.*/ ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_TYPICAL_MAX_SIZE, "AT+UCIND=2" ); pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqControlSignalStrengthIndication ); } else { /* Disable signal level change indication via +CIEV URC.*/ ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_TYPICAL_MAX_SIZE, "AT+UCIND=0" ); pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqControlSignalStrengthIndication ); } cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_RegisterUrcSignalStrengthChangedCallback( CellularHandle_t cellularHandle, CellularUrcSignalStrengthChangedCallback_t signalStrengthChangedCallback, void * pCallbackContext ) { CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; /* pContext is checked in the common library. */ cellularStatus = Cellular_CommonRegisterUrcSignalStrengthChangedCallback( cellularHandle, signalStrengthChangedCallback, pCallbackContext ); if( cellularStatus == CELLULAR_SUCCESS ) { if( signalStrengthChangedCallback != NULL ) { cellularStatus = controlSignalStrengthIndication( pContext, true ); } else { cellularStatus = controlSignalStrengthIndication( pContext, false ); } } return cellularStatus; } /*-----------------------------------------------------------*/ /* Resolve Domain name to IP address */ static CellularPktStatus_t _Cellular_RecvFuncResolveDomainToIpAddress( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ) { char * pRespLine = NULL; char * pResolvedIpAddress = ( char * ) pData; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; const CellularATCommandLine_t * pCommnadItem = NULL; char * pToken = NULL; if( pContext == NULL ) { LogError( ( "_Cellular_RecvFuncResolveDomainToIpAddress: invalid context" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else if( ( pResolvedIpAddress == NULL ) || ( dataLen != CELLULAR_IP_ADDRESS_MAX_SIZE ) ) { pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else if( pAtResp == NULL ) { LogError( ( "_Cellular_RecvFuncResolveDomainToIpAddress: Response is invalid" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else if( ( pAtResp->pItm == NULL ) || ( pAtResp->pItm->pLine == NULL ) ) { LogError( ( "_Cellular_RecvFuncResolveDomainToIpAddress: Address not resolved" ) ); pktStatus = CELLULAR_PKT_STATUS_OK; } else { pRespLine = pAtResp->pItm->pLine; pCommnadItem = pAtResp->pItm; while( pCommnadItem != NULL ) { pRespLine = pCommnadItem->pLine; LogDebug( ( "_Cellular_RecvFuncResolveDomainToIpAddress: pRespLine [%s]", pRespLine ) ); /* Removing all the Spaces in the AT Response. */ atCoreStatus = Cellular_ATRemoveAllWhiteSpaces( pRespLine ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemovePrefix( &pRespLine ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemoveAllDoubleQuote( pRespLine ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pRespLine, &pToken ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { ( void ) strncpy( pResolvedIpAddress, pToken, dataLen ); LogDebug( ( "_Cellular_RecvFuncResolveDomainToIpAddress: Resolved IP Address: [%s]", pResolvedIpAddress ) ); } } pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); if( pktStatus != CELLULAR_PKT_STATUS_OK ) { LogError( ( "_Cellular_RecvFuncResolveDomainToIpAddress: parse %s failed", pRespLine ) ); break; } pCommnadItem = pCommnadItem->pNext; } } return pktStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_GetHostByName( CellularHandle_t cellularHandle, uint8_t contextId, const char * pcHostName, char * pResolvedAddress ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_MAX_SIZE ] = { '\0' }; CellularAtReq_t atReqQueryDns = { NULL, CELLULAR_AT_MULTI_WITH_PREFIX, "+UDNSRN", _Cellular_RecvFuncResolveDomainToIpAddress, NULL, CELLULAR_IP_ADDRESS_MAX_SIZE, }; ( void ) contextId; /* pContext is checked in _Cellular_CheckLibraryStatus function. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); atReqQueryDns.pAtCmd = cmdBuf; atReqQueryDns.pData = pResolvedAddress; if( cellularStatus != CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_GetHostByName: _Cellular_CheckLibraryStatus failed" ) ); } else if( ( pcHostName == NULL ) || ( pResolvedAddress == NULL ) ) { LogError( ( "Cellular_GetHostByName: Bad input Parameter " ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } if( cellularStatus == CELLULAR_SUCCESS ) { ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_MAX_SIZE, "AT+UDNSRN=0,\"%s\"", pcHostName ); pktStatus = _Cellular_TimeoutAtcmdRequestWithCallback( pContext, atReqQueryDns, DNS_QUERY_REQ_TIMEOUT_MS ); if( pktStatus != CELLULAR_PKT_STATUS_OK ) { LogError( ( "Cellular_GetHostByName: couldn't resolve host name" ) ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } } return cellularStatus; } /*-----------------------------------------------------------*/ /* Get PDN context APN name*/ static CellularPktStatus_t _Cellular_RecvFuncGetPdpContextSettings( CellularContext_t * pContext, const CellularATCommandResponse_t * pAtResp, void * pData, uint16_t dataLen ) { char * pRespLine = NULL; CellularPdnContextInfo_t * pPDPContextsInfo = ( CellularPdnContextInfo_t * ) pData; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; const CellularATCommandLine_t * pCommnadItem = NULL; uint8_t tokenIndex = 0; uint8_t contextId = 0; int32_t tempValue = 0; char * pToken = NULL; if( pContext == NULL ) { LogError( ( "_Cellular_RecvFuncGetPdpContextSettings: invalid context" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else if( ( pPDPContextsInfo == NULL ) || ( dataLen != sizeof( CellularPdnContextInfo_t ) ) ) { pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM; } else if( pAtResp == NULL ) { LogError( ( "_Cellular_RecvFuncGetPdpContextSettings: Response is invalid" ) ); pktStatus = CELLULAR_PKT_STATUS_FAILURE; } else if( ( pAtResp->pItm == NULL ) || ( pAtResp->pItm->pLine == NULL ) ) { LogError( ( "_Cellular_RecvFuncGetPdpContextSettings: no PDN context available" ) ); pktStatus = CELLULAR_PKT_STATUS_OK; } else { pRespLine = pAtResp->pItm->pLine; pCommnadItem = pAtResp->pItm; while( pCommnadItem != NULL ) { pRespLine = pCommnadItem->pLine; LogDebug( ( "_Cellular_RecvFuncGetPdpContextSettings: pRespLine [%s]", pRespLine ) ); /* Removing all the Spaces in the AT Response. */ atCoreStatus = Cellular_ATRemoveAllWhiteSpaces( pRespLine ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemovePrefix( &pRespLine ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATRemoveAllDoubleQuote( pRespLine ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { atCoreStatus = Cellular_ATGetNextTok( &pRespLine, &pToken ); } if( atCoreStatus == CELLULAR_AT_SUCCESS ) { tokenIndex = 0; while( ( pToken != NULL ) && ( atCoreStatus == CELLULAR_AT_SUCCESS ) ) { switch( tokenIndex ) { case ( CELLULAR_PDN_STATUS_POS_CONTEXT_ID ): LogDebug( ( "_Cellular_RecvFuncGetPdpContextSettings: Context Id pToken: %s", pToken ) ); atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) { if( ( tempValue >= ( int32_t ) CELLULAR_PDN_CONTEXT_ID_MIN ) && ( tempValue <= ( int32_t ) MAX_PDP_CONTEXTS ) ) { contextId = ( uint8_t ) tempValue; pPDPContextsInfo->contextsPresent[ contextId - 1 ] = true; LogDebug( ( "_Cellular_RecvFuncGetPdpContextSettings: Context Id: %d", contextId ) ); } else { LogError( ( "_Cellular_RecvFuncGetPdpContextSettings: Invalid Context Id. Token %s", pToken ) ); atCoreStatus = CELLULAR_AT_ERROR; } } break; case ( CELLULAR_PDN_STATUS_POS_CONTEXT_TYPE ): LogDebug( ( "_Cellular_RecvFuncGetPdpContextSettings: Context Type pToken: %s", pToken ) ); ( void ) memcpy( ( void * ) pPDPContextsInfo->ipType[ contextId - 1 ], ( void * ) pToken, CELULAR_PDN_CONTEXT_TYPE_MAX_SIZE + 1U ); break; case ( CELLULAR_PDN_STATUS_POS_APN_NAME ): LogDebug( ( "_Cellular_RecvFuncGetPdpContextSettings: Context APN name pToken: %s", pToken ) ); ( void ) memcpy( ( void * ) pPDPContextsInfo->apnName[ contextId - 1 ], ( void * ) pToken, CELLULAR_APN_MAX_SIZE + 1U ); break; case ( CELLULAR_PDN_STATUS_POS_IP_ADDRESS ): LogDebug( ( "_Cellular_RecvFuncGetPdpContextSettings: Context IP address pToken: %s", pToken ) ); ( void ) memcpy( ( void * ) pPDPContextsInfo->ipAddress[ contextId - 1 ], ( void * ) pToken, CELLULAR_IP_ADDRESS_MAX_SIZE + 1U ); break; default: break; } tokenIndex++; if( Cellular_ATGetNextTok( &pRespLine, &pToken ) != CELLULAR_AT_SUCCESS ) { break; } } } } pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); if( pktStatus != CELLULAR_PKT_STATUS_OK ) { LogError( ( "_Cellular_RecvFuncGetPdpContextSettings: parse %s failed", pRespLine ) ); break; } pCommnadItem = pCommnadItem->pNext; } } return pktStatus; } /*-----------------------------------------------------------*/ /* Set PDN APN name and Authentication setting */ CellularError_t Cellular_SetPdnConfig( CellularHandle_t cellularHandle, uint8_t contextId, const CellularPdnConfig_t * pPdnConfig ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_MAX_SIZE ] = { '\0' }; char pPdpTypeStr[ CELULAR_PDN_CONTEXT_TYPE_MAX_SIZE ] = { '\0' }; uint32_t i = 0; CellularPdnContextInfo_t pdpContextsInfo = { 0 }; CellularPdnContextInfo_t * pPdpContextsInfo = &pdpContextsInfo; CellularAtReq_t atReqSetPdn = { NULL, CELLULAR_AT_NO_RESULT, NULL, NULL, NULL, 0, }; atReqSetPdn.pAtCmd = cmdBuf; if( pPdnConfig == NULL ) { LogDebug( ( "Cellular_SetPdnConfig: Input parameter is NULL" ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else { switch( pPdnConfig->pdnContextType ) { case CELLULAR_PDN_CONTEXT_IPV4: ( void ) strncpy( pPdpTypeStr, "IP", 3U ); /* 3U is the length of "IP" + '\0'. */ break; case CELLULAR_PDN_CONTEXT_IPV6: ( void ) strncpy( pPdpTypeStr, "IPV6", 5U ); /* 5U is the length of "IPV6" + '\0'. */ break; case CELLULAR_PDN_CONTEXT_IPV4V6: ( void ) strncpy( pPdpTypeStr, "IPV4V6", 7U ); /* 7U is the length of "IPV4V6" + '\0'. */ break; default: LogDebug( ( "Cellular_SetPdnConfig: Invalid pdn context type %d", CELLULAR_PDN_CONTEXT_IPV4V6 ) ); cellularStatus = CELLULAR_BAD_PARAMETER; break; } } if( cellularStatus == CELLULAR_SUCCESS ) { cellularStatus = _Cellular_IsValidPdn( contextId ); } if( cellularStatus == CELLULAR_SUCCESS ) { /* Make sure the library is open. */ cellularStatus = _Cellular_CheckLibraryStatus( pContext ); } if( cellularStatus == CELLULAR_SUCCESS ) { /* Check current APN name and IP type of contextId first to avoid unneccessary network de-registration. */ /* TODO: This implementation currently assumes only one context in list. Need to add complete contexts list parsing */ CellularAtReq_t atReqGetCurrentApnName = { "AT+CGDCONT?", CELLULAR_AT_MULTI_WITH_PREFIX, "+CGDCONT", _Cellular_RecvFuncGetPdpContextSettings, NULL, sizeof( CellularPdnContextInfo_t ), }; atReqGetCurrentApnName.pData = pPdpContextsInfo; pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqGetCurrentApnName ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); if( cellularStatus == CELLULAR_SUCCESS ) { LogDebug( ( "Cellular_SetPdnConfig: Listing operator and context details below." ) ); for( i = 0U; i < ( MAX_PDP_CONTEXTS - 1 ); i++ ) { /* Print only those contexts that are present in +CGDCONT response */ if( pdpContextsInfo.contextsPresent[ i ] ) { LogDebug( ( "Context [%d], IP Type [%s], APN Name [%s], IP Address [%s]\r\n", i + 1, pdpContextsInfo.ipType[ i ], ( char * ) pdpContextsInfo.apnName, ( char * ) pdpContextsInfo.ipAddress ) ); } } } } if( cellularStatus == CELLULAR_SUCCESS ) { /* Form the AT command. */ /* The return value of snprintf is not used. * The max length of the string is fixed and checked offline. */ /* coverity[misra_c_2012_rule_21_6_violation]. */ if( ( strstr( pdpContextsInfo.apnName[ contextId - 1 ], pPdnConfig->apnName ) == NULL ) || ( strcmp( pdpContextsInfo.ipType[ contextId - 1 ], pPdpTypeStr ) != 0 ) ) { if( strcmp( pdpContextsInfo.ipType[ contextId - 1 ], pPdpTypeStr ) != 0 ) { LogInfo( ( "Cellular_SetPdnConfig: Setting new IPv (Module IPv:%s != %s)\r\n", pdpContextsInfo.ipType[ contextId - 1 ], pPdpTypeStr ) ); } if( strstr( pdpContextsInfo.apnName[ contextId - 1 ], pPdnConfig->apnName ) == NULL ) { LogInfo( ( "Cellular_SetPdnConfig: Setting new APN (Module APN:%s != %s)\r\n", pdpContextsInfo.apnName[ contextId - 1 ], pPdnConfig->apnName ) ); } /* TODO: Add support if + UAUTHREQ is PAP / CHAP */ ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_MAX_SIZE, "%s%d,\"%s\",\"%s\",,0,0;%s%d,%d%s", /*,\"%s\",\"%s\" TODO: add if +UAUTHREQ is PAP/CHAP */ "AT+COPS=2;+CGDCONT=", contextId, pPdpTypeStr, pPdnConfig->apnName, "+UAUTHREQ=", contextId, pPdnConfig->pdnAuthType, ";+COPS=0" ); /*pPdnConfig->username, */ /*pPdnConfig->password); */ pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqSetPdn ); if( pktStatus != CELLULAR_PKT_STATUS_OK ) { LogError( ( "Cellular_SetPdnConfig: can't set PDN, cmdBuf:%s, PktRet: %d", cmdBuf, pktStatus ) ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } } else { LogInfo( ( "Cellular_SetPdnConfig: APN and IPv already set.\r\n" ) ); } } return cellularStatus; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_SetPsmSettings( CellularHandle_t cellularHandle, const CellularPsmSettings_t * pPsmSettings ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_MAX_SIZE ] = { '\0' }; CellularAtReq_t atReqSetPsm = { NULL, CELLULAR_AT_NO_RESULT, NULL, NULL, NULL, 0 }; atReqSetPsm.pAtCmd = cmdBuf; cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus != CELLULAR_SUCCESS ) { LogError( ( "Cellular_SetPsmSettings: _Cellular_CheckLibraryStatus failed" ) ); } else if( pPsmSettings == NULL ) { LogError( ( "Cellular_SetPsmSettings : Bad parameter" ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else { if( pPsmSettings->mode == 1 ) { /* * SARA-R4: To change the command setting issue AT+COPS=2 or AT+CFUN=0 to deregister the module from * network, issue the +CPSMS command and reboot the module in order to apply the new configuration. */ /* After PSM mode active, press "PWR_ON" key to awake modem or T3412 timer is expired. */ ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_MAX_SIZE, "AT+CFUN=0" ); pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqSetPsm ); if( pktStatus == CELLULAR_PKT_STATUS_OK ) { cellularStatus = Cellular_CommonSetPsmSettings( cellularHandle, pPsmSettings ); if( cellularStatus == CELLULAR_SUCCESS ) { cellularStatus = rebootCellularModem( pContext, false, true ); } else { LogError( ( "Cellular_SetPsmSettings: Unable to set PSM settings." ) ); } } } else { cellularStatus = Cellular_CommonSetPsmSettings( cellularHandle, pPsmSettings ); } } return cellularStatus; } /*-----------------------------------------------------------*/ static CellularError_t _Cellular_isSockOptSupport( CellularSocketOptionLevel_t optionLevel, CellularSocketOption_t option ) { CellularError_t err = CELLULAR_UNSUPPORTED; if( ( optionLevel == CELLULAR_SOCKET_OPTION_LEVEL_TRANSPORT ) && ( ( option == CELLULAR_SOCKET_OPTION_SEND_TIMEOUT ) || ( option == CELLULAR_SOCKET_OPTION_RECV_TIMEOUT ) || ( option == CELLULAR_SOCKET_OPTION_PDN_CONTEXT_ID ) ) ) { err = CELLULAR_SUCCESS; } else { LogWarn( ( "Cellular_SocketSetSockOpt: Option [Level:option=%d:%d] not supported in SARA R4", optionLevel, option ) ); } return err; } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ /* coverity[misra_c_2012_rule_8_7_violation] */ CellularError_t Cellular_SetEidrxSettings( CellularHandle_t cellularHandle, const CellularEidrxSettings_t * pEidrxSettings ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK; char cmdBuf[ CELLULAR_AT_CMD_MAX_SIZE ] = { '\0' }; CellularAtReq_t atReqSetEidrx = { NULL, CELLULAR_AT_NO_RESULT, NULL, NULL, NULL, 0 }; atReqSetEidrx.pAtCmd = cmdBuf; cellularStatus = _Cellular_CheckLibraryStatus( pContext ); if( cellularStatus != CELLULAR_SUCCESS ) { LogError( ( "Cellular_SetEidrxSettings: _Cellular_CheckLibraryStatus failed" ) ); } else if( pEidrxSettings == NULL ) { LogError( ( "Cellular_SetEidrxSettings : Bad parameter" ) ); cellularStatus = CELLULAR_BAD_PARAMETER; } else { if( ( pEidrxSettings->mode == 1 ) || ( pEidrxSettings->mode == 2 ) ) { /* * SARA-R4: To change the command setting issue AT+COPS=2 or AT+CFUN=0 to deregister the module from * network, issue the +CEDRXS command and reboot the module in order to apply the new configuration. */ ( void ) snprintf( cmdBuf, CELLULAR_AT_CMD_MAX_SIZE, "AT+CFUN=0" ); pktStatus = _Cellular_AtcmdRequestWithCallback( pContext, atReqSetEidrx ); if( pktStatus == CELLULAR_PKT_STATUS_OK ) { cellularStatus = Cellular_CommonSetEidrxSettings( cellularHandle, pEidrxSettings ); if( cellularStatus == CELLULAR_SUCCESS ) { cellularStatus = rebootCellularModem( pContext, true, false ); } else { LogError( ( "Cellular_SetEidrxSettings: Unable to set Eidrx settings." ) ); } } } else { cellularStatus = Cellular_CommonSetEidrxSettings( cellularHandle, pEidrxSettings ); } } return cellularStatus; } /*-----------------------------------------------------------*/ CellularError_t Cellular_Init( CellularHandle_t * pCellularHandle, const CellularCommInterface_t * pCommInterface ) { CellularTokenTable_t cellularTokenTable = { 0 }; cellularTokenTable.pCellularUrcHandlerTable = CellularUrcHandlerTable; cellularTokenTable.cellularPrefixToParserMapSize = CellularUrcHandlerTableSize; cellularTokenTable.pCellularSrcTokenErrorTable = CellularSrcTokenErrorTable; cellularTokenTable.cellularSrcTokenErrorTableSize = CellularSrcTokenErrorTableSize; cellularTokenTable.pCellularSrcTokenSuccessTable = CellularSrcTokenSuccessTable; cellularTokenTable.cellularSrcTokenSuccessTableSize = CellularSrcTokenSuccessTableSize; cellularTokenTable.pCellularUrcTokenWoPrefixTable = CellularUrcTokenWoPrefixTable; cellularTokenTable.cellularUrcTokenWoPrefixTableSize = CellularUrcTokenWoPrefixTableSize; cellularTokenTable.pCellularSrcExtraTokenSuccessTable = NULL; cellularTokenTable.cellularSrcExtraTokenSuccessTableSize = 0; return Cellular_CommonInit( pCellularHandle, pCommInterface, &cellularTokenTable ); } /*-----------------------------------------------------------*/ /* FreeRTOS Cellular Library API. */ CellularError_t Cellular_SocketSetSockOpt( CellularHandle_t cellularHandle, CellularSocketHandle_t socketHandle, CellularSocketOptionLevel_t optionLevel, CellularSocketOption_t option, const uint8_t * pOptionValue, uint32_t optionValueLength ) { CellularError_t err = CELLULAR_SUCCESS; err = _Cellular_isSockOptSupport( optionLevel, option ); if( err == CELLULAR_SUCCESS ) { err = Cellular_CommonSocketSetSockOpt( cellularHandle, socketHandle, optionLevel, option, pOptionValue, optionValueLength ); } return err; } /*-----------------------------------------------------------*/