diff --git a/source/cellular_3gpp_api.c b/source/cellular_3gpp_api.c index c668f149..fc8dae18 100644 --- a/source/cellular_3gpp_api.c +++ b/source/cellular_3gpp_api.c @@ -796,6 +796,7 @@ static CellularPktStatus_t _Cellular_RecvFuncGetNetworkReg( CellularContext_t * CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; CellularNetworkRegType_t regType = CELLULAR_REG_TYPE_UNKNOWN; CellularATCommandLine_t * pCommandLine = NULL; + bool isUrc = false; if( pContext == NULL ) { @@ -823,20 +824,15 @@ static CellularPktStatus_t _Cellular_RecvFuncGetNetworkReg( CellularContext_t * /* Assumption is that the data is null terminated so we don't need the dataLen. */ _Cellular_LockAtDataMutex( pContext ); - if( regResponseIsUrc( pPregLine ) == true ) - { - /* Remove the prefix for URC handler. */ - atCoreStatus = Cellular_ATRemovePrefix( &pPregLine ); - pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); + isUrc = regResponseIsUrc( pPregLine ); - if( pktStatus == CELLULAR_PKT_STATUS_OK ) - { - pktStatus = _Cellular_ParseRegStatus( pContext, pPregLine, true, regType ); - } - } - else + /* Remove the prefix for URC handler. */ + atCoreStatus = Cellular_ATRemovePrefix( &pPregLine ); + pktStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); + + if( pktStatus == CELLULAR_PKT_STATUS_OK ) { - pktStatus = _Cellular_ParseRegStatus( pContext, pPregLine, false, regType ); + pktStatus = _Cellular_ParseRegStatus( pContext, pPregLine, isUrc, regType ); } _Cellular_UnlockAtDataMutex( pContext ); @@ -2132,6 +2128,8 @@ void _Cellular_InitAtData( CellularContext_t * pContext, pLibAtData->cellId = 0xFFFFFFFFU; pLibAtData->rat = CELLULAR_RAT_INVALID; pLibAtData->rac = 0xFF; + pLibAtData->periodicTauValue = 0xFFFFFFFFU; + pLibAtData->activeTimeValue = 0xFFFFFFFFU; } /*-----------------------------------------------------------*/ diff --git a/source/cellular_3gpp_urc_handler.c b/source/cellular_3gpp_urc_handler.c index 7efdaa20..07dcbd4a 100644 --- a/source/cellular_3gpp_urc_handler.c +++ b/source/cellular_3gpp_urc_handler.c @@ -49,34 +49,58 @@ /*-----------------------------------------------------------*/ +#define CELLULAR_REG_POS_URC_MODE ( 1U ) /* Only specified when queried, not in URC */ #define CELLULAR_REG_POS_STAT ( 2U ) #define CELLULAR_REG_POS_LAC_TAC ( 3U ) #define CELLULAR_REG_POS_CELL_ID ( 4U ) #define CELLULAR_REG_POS_RAT ( 5U ) #define CELLULAR_REG_POS_REJ_TYPE ( 6U ) #define CELLULAR_REG_POS_REJ_CAUSE ( 7U ) +#define CELLULAR_REG_POS_URC_ACTIVE_TIME ( 8U ) +#define CELLULAR_REG_POS_URC_PERIODIC_TAU_RAU ( 9U ) + +#define CELLULAR_REG_URC_MODE_DISABLED ( 0U ) +#define CELLULAR_REG_URC_MODE_STAT_ENABLED ( 1U ) +#define CELLULAR_REG_URC_MODE_STAT_LOCATION_ENABLED ( 2U ) +#define CELLULAR_REG_URC_MODE_STAT_LOCATION_PSM_ENABLED ( 4U ) +#define CELLULAR_REG_URC_MODE_UNKNOWN ( 0xFFU ) /*-----------------------------------------------------------*/ +static CellularATError_t _parseUrcModeInRegStatus( const char * pToken, + uint8_t * urcMode ); static CellularPktStatus_t _parseRegStatusInRegStatusParsing( CellularContext_t * pContext, CellularNetworkRegType_t regType, const char * pToken, cellularAtData_t * pLibAtData ); static CellularPktStatus_t _parseLacTacInRegStatus( CellularNetworkRegType_t regType, const char * pToken, - cellularAtData_t * pLibAtData ); + cellularAtData_t * pLibAtData, + bool allowEmpty ); static CellularPktStatus_t _parseCellIdInRegStatus( const char * pToken, - cellularAtData_t * pLibAtData ); + cellularAtData_t * pLibAtData, + bool allowEmpty ); static CellularPktStatus_t _parseRatInfoInRegStatus( const char * pToken, - cellularAtData_t * pLibAtData ); + cellularAtData_t * pLibAtData, + bool allowEmpty ); static CellularPktStatus_t _parseRejectTypeInRegStatus( CellularNetworkRegType_t regType, const char * pToken, - cellularAtData_t * pLibAtData ); + cellularAtData_t * pLibAtData, + bool allowEmpty ); static CellularPktStatus_t _parseRejectCauseInRegStatus( CellularNetworkRegType_t regType, const char * pToken, - cellularAtData_t * pLibAtData ); + cellularAtData_t * pLibAtData, + bool allowEmpty ); +static CellularPktStatus_t _parseActiveTimeInRegStatus( const char * pToken, + cellularAtData_t * pLibAtData, + bool allowEmpty ); +static CellularPktStatus_t _parsePeriodicTauInRegStatus( const char * pToken, + cellularAtData_t * pLibAtData, + bool allowEmpty ); static CellularPktStatus_t _regStatusSwitchParsingFunc( CellularContext_t * pContext, uint8_t i, CellularNetworkRegType_t regType, + bool isUrc, + uint8_t urcMode, const char * pToken, cellularAtData_t * pLibAtData ); static void _regStatusGenerateLog( char * pRegPayload, @@ -91,6 +115,39 @@ static bool _Cellular_RegEventStatus( const cellularAtData_t * pLibAtData, /*-----------------------------------------------------------*/ +static CellularATError_t _parseUrcModeInRegStatus( const char * pToken, + uint8_t * urcMode ) +{ + int32_t var = 0; + CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; + + if( urcMode == NULL ) + { + atCoreStatus = CELLULAR_AT_BAD_PARAMETER; + } + else + { + atCoreStatus = Cellular_ATStrtoi( pToken, 10, &var ); + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) + { + if( ( var >= 0 ) && ( var <= UINT8_MAX ) ) + { + *urcMode = ( uint8_t ) var; + } + else + { + atCoreStatus = CELLULAR_AT_ERROR; + LogError( ( "Error in processing URC mode in reg status. Token '%s'", pToken ) ); + } + } + } + + return atCoreStatus; +} + +/*-----------------------------------------------------------*/ + static CellularPktStatus_t _parseRegStatusInRegStatusParsing( CellularContext_t * pContext, CellularNetworkRegType_t regType, const char * pToken, @@ -168,115 +225,165 @@ static CellularPktStatus_t _parseRegStatusInRegStatusParsing( CellularContext_t static CellularPktStatus_t _parseLacTacInRegStatus( CellularNetworkRegType_t regType, const char * pToken, - cellularAtData_t * pLibAtData ) + cellularAtData_t * pLibAtData, + bool allowEmpty ) { int32_t tempValue = 0; uint16_t var = 0; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; CellularPktStatus_t packetStatus = CELLULAR_PKT_STATUS_OK; + bool skipParsing = false; - atCoreStatus = Cellular_ATStrtoi( pToken, 16, &tempValue ); - - if( atCoreStatus == CELLULAR_AT_SUCCESS ) + if( ( pToken != NULL ) && ( pToken[0] == '\0' ) ) { - if( ( tempValue >= 0 ) && ( tempValue <= UINT16_MAX ) ) + if( allowEmpty == true ) { - var = ( uint16_t ) tempValue; + skipParsing = true; } else { - atCoreStatus = CELLULAR_AT_ERROR; + LogDebug( ( "Unexpected empty LAC/TAC in Registration Status" ) ); } } - if( atCoreStatus == CELLULAR_AT_SUCCESS ) + if( skipParsing != true ) { - /* Parsing Location area code for CREG or CGREG. */ - if( ( regType == CELLULAR_REG_TYPE_CREG ) || ( regType == CELLULAR_REG_TYPE_CGREG ) ) - { - pLibAtData->lac = ( uint16_t ) var; - } - /* Parsing Tracking area code for CEREG. */ - else if( regType == CELLULAR_REG_TYPE_CEREG ) + atCoreStatus = Cellular_ATStrtoi( pToken, 16, &tempValue ); + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) { - pLibAtData->tac = ( uint16_t ) var; + if( ( tempValue >= 0 ) && ( tempValue <= UINT16_MAX ) ) + { + var = ( uint16_t ) tempValue; + } + else + { + atCoreStatus = CELLULAR_AT_ERROR; + } } - else + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) { - /* Empty else MISRA 15.7 */ + /* Parsing Location area code for CREG or CGREG. */ + if( ( regType == CELLULAR_REG_TYPE_CREG ) || ( regType == CELLULAR_REG_TYPE_CGREG ) ) + { + pLibAtData->lac = ( uint16_t ) var; + } + /* Parsing Tracking area code for CEREG. */ + else if( regType == CELLULAR_REG_TYPE_CEREG ) + { + pLibAtData->tac = ( uint16_t ) var; + } + else + { + /* Empty else MISRA 15.7 */ + } } + + packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); } - packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); return packetStatus; } /*-----------------------------------------------------------*/ static CellularPktStatus_t _parseCellIdInRegStatus( const char * pToken, - cellularAtData_t * pLibAtData ) + cellularAtData_t * pLibAtData, + bool allowEmpty ) { - int32_t tempValue = 0; + uint32_t tempValue = 0; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; CellularPktStatus_t packetStatus = CELLULAR_PKT_STATUS_OK; + bool skipParsing = false; - atCoreStatus = Cellular_ATStrtoi( pToken, 16, &tempValue ); + if( ( pToken != NULL ) && ( pToken[0] == '\0' ) ) + { + if( allowEmpty == true ) + { + skipParsing = true; + } + else + { + LogDebug( ( "Unexpected empty Cell ID in Registration Status" ) ); + } + } - if( atCoreStatus == CELLULAR_AT_SUCCESS ) + if( skipParsing != true ) { - if( tempValue >= 0 ) + atCoreStatus = Cellular_ATStrtoui( pToken, 16, &tempValue ); + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) { - pLibAtData->cellId = ( uint32_t ) tempValue; + pLibAtData->cellId = tempValue; } else { - LogError( ( "Error in processing Cell Id. Token %s", pToken ) ); - atCoreStatus = CELLULAR_AT_ERROR; + LogError( ( "Error in processing Cell Id. Token '%s'", pToken ) ); } + + packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); } - packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); return packetStatus; } /*-----------------------------------------------------------*/ static CellularPktStatus_t _parseRatInfoInRegStatus( const char * pToken, - cellularAtData_t * pLibAtData ) + cellularAtData_t * pLibAtData, + bool allowEmpty ) { int32_t var = 0; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; CellularPktStatus_t packetStatus = CELLULAR_PKT_STATUS_OK; + bool skipParsing = false; - atCoreStatus = Cellular_ATStrtoi( pToken, 10, &var ); - - if( atCoreStatus == CELLULAR_AT_SUCCESS ) + if( ( pToken != NULL ) && ( pToken[0] == '\0' ) ) { - if( var >= ( int32_t ) CELLULAR_RAT_MAX ) - { - atCoreStatus = CELLULAR_AT_ERROR; - LogError( ( "Error in processing RAT. Token %s", pToken ) ); - } - else if( ( var == ( int32_t ) CELLULAR_RAT_GSM ) || ( var == ( int32_t ) CELLULAR_RAT_EDGE ) || - ( var == ( int32_t ) CELLULAR_RAT_CATM1 ) || ( var == ( int32_t ) CELLULAR_RAT_NBIOT ) ) + if( allowEmpty == true ) { - /* MISRA Ref 10.5.1 [Essential type casting] */ - /* More details at: https://github.com/FreeRTOS/FreeRTOS-Cellular-Interface/blob/main/MISRA.md#rule-105 */ - /* coverity[misra_c_2012_rule_10_5_violation] */ - pLibAtData->rat = ( CellularRat_t ) var; + skipParsing = true; } - else if( var == ( int32_t ) CELLULAR_RAT_LTE ) + else { - /* Some cellular module use 7 : CELLULAR_RAT_LTE to indicate CAT-M1. */ - pLibAtData->rat = ( CellularRat_t ) CELLULAR_RAT_LTE; + LogDebug( ( "Unexpected empty RAT Info in Registration Status" ) ); } - else + } + + if( skipParsing != true ) + { + atCoreStatus = Cellular_ATStrtoi( pToken, 10, &var ); + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) { - pLibAtData->rat = CELLULAR_RAT_INVALID; + if( var >= ( int32_t ) CELLULAR_RAT_MAX ) + { + atCoreStatus = CELLULAR_AT_ERROR; + LogError( ( "Error in processing RAT. Token '%s'", pToken ) ); + } + else if( ( var == ( int32_t ) CELLULAR_RAT_GSM ) || ( var == ( int32_t ) CELLULAR_RAT_EDGE ) || + ( var == ( int32_t ) CELLULAR_RAT_CATM1 ) || ( var == ( int32_t ) CELLULAR_RAT_NBIOT ) ) + { + /* MISRA Ref 10.5.1 [Essential type casting] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Cellular-Interface/blob/main/MISRA.md#rule-105 */ + /* coverity[misra_c_2012_rule_10_5_violation] */ + pLibAtData->rat = ( CellularRat_t ) var; + } + else if( var == ( int32_t ) CELLULAR_RAT_LTE ) + { + /* Some cellular module use 7 : CELLULAR_RAT_LTE to indicate CAT-M1. */ + pLibAtData->rat = ( CellularRat_t ) CELLULAR_RAT_LTE; + } + else + { + pLibAtData->rat = CELLULAR_RAT_INVALID; + } } + + packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); } - packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); return packetStatus; } @@ -284,51 +391,69 @@ static CellularPktStatus_t _parseRatInfoInRegStatus( const char * pToken, static CellularPktStatus_t _parseRejectTypeInRegStatus( CellularNetworkRegType_t regType, const char * pToken, - cellularAtData_t * pLibAtData ) + cellularAtData_t * pLibAtData, + bool allowEmpty ) { int32_t tempValue = 0; uint8_t rejType = 0; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; CellularPktStatus_t packetStatus = CELLULAR_PKT_STATUS_OK; + bool skipParsing = false; - atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); - - if( atCoreStatus == CELLULAR_AT_SUCCESS ) + if( ( pToken != NULL ) && ( pToken[0] == '\0' ) ) { - if( ( tempValue >= 0 ) && ( tempValue <= ( int32_t ) UINT8_MAX ) ) + if( allowEmpty == true ) { - rejType = ( uint8_t ) tempValue; + skipParsing = true; } else { - atCoreStatus = CELLULAR_AT_ERROR; + LogDebug( ( "Unexpected empty Reject Type in Registration Status" ) ); } } - if( atCoreStatus == CELLULAR_AT_SUCCESS ) + if( skipParsing != true ) { - if( regType == CELLULAR_REG_TYPE_CREG ) + atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) { - /* Reject Type is only stored if the registration status is denied. */ - if( pLibAtData->csRegStatus == REGISTRATION_STATUS_REGISTRATION_DENIED ) + if( ( tempValue >= 0 ) && ( tempValue <= ( int32_t ) UINT8_MAX ) ) { - pLibAtData->csRejectType = rejType; + rejType = ( uint8_t ) tempValue; } - } - else if( ( regType == CELLULAR_REG_TYPE_CGREG ) || ( regType == CELLULAR_REG_TYPE_CEREG ) ) - { - if( pLibAtData->psRegStatus == REGISTRATION_STATUS_REGISTRATION_DENIED ) + else { - pLibAtData->psRejectType = rejType; + atCoreStatus = CELLULAR_AT_ERROR; } } - else + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) { - /* Empty else MISRA 15.7 */ + if( regType == CELLULAR_REG_TYPE_CREG ) + { + /* Reject Type is only stored if the registration status is denied. */ + if( pLibAtData->csRegStatus == REGISTRATION_STATUS_REGISTRATION_DENIED ) + { + pLibAtData->csRejectType = rejType; + } + } + else if( ( regType == CELLULAR_REG_TYPE_CGREG ) || ( regType == CELLULAR_REG_TYPE_CEREG ) ) + { + if( pLibAtData->psRegStatus == REGISTRATION_STATUS_REGISTRATION_DENIED ) + { + pLibAtData->psRejectType = rejType; + } + } + else + { + /* Empty else MISRA 15.7 */ + } } + + packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); } - packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); return packetStatus; } @@ -336,50 +461,161 @@ static CellularPktStatus_t _parseRejectTypeInRegStatus( CellularNetworkRegType_t static CellularPktStatus_t _parseRejectCauseInRegStatus( CellularNetworkRegType_t regType, const char * pToken, - cellularAtData_t * pLibAtData ) + cellularAtData_t * pLibAtData, + bool allowEmpty ) { int32_t tempValue = 0; uint8_t rejCause = 0; CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; CellularPktStatus_t packetStatus = CELLULAR_PKT_STATUS_OK; + bool skipParsing = false; - atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); - - if( atCoreStatus == CELLULAR_AT_SUCCESS ) + if( ( pToken != NULL ) && ( pToken[0] == '\0' ) ) { - if( ( tempValue >= 0 ) && ( tempValue <= ( int32_t ) UINT8_MAX ) ) + if( allowEmpty == true ) { - rejCause = ( uint8_t ) tempValue; + skipParsing = true; } else { - atCoreStatus = CELLULAR_AT_ERROR; + LogDebug( ( "Unexpected empty Reject Cause in Registration Status" ) ); } } - if( atCoreStatus == CELLULAR_AT_SUCCESS ) + if( skipParsing != true ) { - if( regType == CELLULAR_REG_TYPE_CREG ) + atCoreStatus = Cellular_ATStrtoi( pToken, 10, &tempValue ); + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) + { + if( ( tempValue >= 0 ) && ( tempValue <= ( int32_t ) UINT8_MAX ) ) + { + rejCause = ( uint8_t ) tempValue; + } + else + { + atCoreStatus = CELLULAR_AT_ERROR; + } + } + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) { - if( pLibAtData->csRegStatus == REGISTRATION_STATUS_REGISTRATION_DENIED ) + if( regType == CELLULAR_REG_TYPE_CREG ) + { + if( pLibAtData->csRegStatus == REGISTRATION_STATUS_REGISTRATION_DENIED ) + { + pLibAtData->csRejCause = rejCause; + } + } + else if( ( regType == CELLULAR_REG_TYPE_CGREG ) || ( regType == CELLULAR_REG_TYPE_CEREG ) ) + { + if( pLibAtData->psRegStatus == REGISTRATION_STATUS_REGISTRATION_DENIED ) + { + pLibAtData->psRejCause = rejCause; + } + } + else { - pLibAtData->csRejCause = rejCause; + /* Empty else MISRA 15.7 */ } } - else if( ( regType == CELLULAR_REG_TYPE_CGREG ) || ( regType == CELLULAR_REG_TYPE_CEREG ) ) + + packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); + } + + return packetStatus; +} + +/*-----------------------------------------------------------*/ + +static CellularPktStatus_t _parseActiveTimeInRegStatus( const char * pToken, + cellularAtData_t * pLibAtData, + bool allowEmpty ) +{ + int32_t tempValue = 0; + CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; + CellularPktStatus_t packetStatus = CELLULAR_PKT_STATUS_OK; + bool skipParsing = false; + + if( ( pToken != NULL ) && ( pToken[0] == '\0' ) ) + { + if( allowEmpty == true ) { - if( pLibAtData->psRegStatus == REGISTRATION_STATUS_REGISTRATION_DENIED ) + pLibAtData->activeTimeValue = 0xFFFFFFFF; + skipParsing = true; + } + else + { + LogWarn( ( "Unexpected empty Active Time in Registration Status" ) ); + } + } + + if( skipParsing != true ) + { + atCoreStatus = Cellular_ATStrtoi( pToken, 2, &tempValue ); + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) + { + if( ( tempValue >= 0 ) && ( tempValue <= UINT8_MAX ) ) { - pLibAtData->psRejCause = rejCause; + pLibAtData->activeTimeValue = ( uint32_t ) tempValue; } + else + { + LogError( ( "Error in processing Active Time value. Token '%s'", pToken ) ); + atCoreStatus = CELLULAR_AT_ERROR; + } + } + + packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); + } + + return packetStatus; +} + +/*-----------------------------------------------------------*/ + +static CellularPktStatus_t _parsePeriodicTauInRegStatus( const char * pToken, + cellularAtData_t * pLibAtData, + bool allowEmpty ) +{ + int32_t tempValue = 0; + CellularATError_t atCoreStatus = CELLULAR_AT_SUCCESS; + CellularPktStatus_t packetStatus = CELLULAR_PKT_STATUS_OK; + bool skipParsing = false; + + if( ( pToken != NULL ) && ( pToken[0] == '\0' ) ) + { + if( allowEmpty == true ) + { + pLibAtData->periodicTauValue = 0xFFFFFFFF; + skipParsing = true; } else { - /* Empty else MISRA 15.7 */ + LogWarn( ( "Unexpected empty Periodic TAU in Registration Status" ) ); } } - packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); + if( skipParsing != true ) + { + atCoreStatus = Cellular_ATStrtoi( pToken, 2, &tempValue ); + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) + { + if( ( tempValue >= 0 ) && ( tempValue <= UINT8_MAX ) ) + { + pLibAtData->periodicTauValue = ( uint32_t ) tempValue; + } + else + { + LogError( ( "Error in processing Periodic TAU value. Token '%s'", pToken ) ); + atCoreStatus = CELLULAR_AT_ERROR; + } + } + + packetStatus = _Cellular_TranslateAtCoreStatus( atCoreStatus ); + } return packetStatus; } @@ -389,44 +625,97 @@ static CellularPktStatus_t _parseRejectCauseInRegStatus( CellularNetworkRegType_ static CellularPktStatus_t _regStatusSwitchParsingFunc( CellularContext_t * pContext, uint8_t i, CellularNetworkRegType_t regType, + bool isUrc, + uint8_t urcMode, const char * pToken, cellularAtData_t * pLibAtData ) { CellularPktStatus_t packetStatus = CELLULAR_PKT_STATUS_OK; + bool isUrcOrUrcModePSM = ( ( isUrc == true ) || ( urcMode == CELLULAR_REG_URC_MODE_STAT_LOCATION_PSM_ENABLED ) ); + + if( ( urcMode != CELLULAR_REG_URC_MODE_UNKNOWN ) && ( isUrc == true ) ) + { + LogWarn( ( "Unexpected known URC mode and isURC in Registration Status, i: %hhu, regType: %d, urcMode: %hhu", i, regType, urcMode ) ); + } + + if( ( urcMode == CELLULAR_REG_URC_MODE_STAT_LOCATION_PSM_ENABLED ) && ( regType == CELLULAR_REG_TYPE_CREG ) ) + { + LogWarn( ( "Unexpected PSM URC mode in CREG Registration Status, i: %hhu", i ) ); + } switch( i ) { + case CELLULAR_REG_POS_URC_MODE: + LogError( ( "Unexpected URC Mode Position in Registration: %s", + ( ( pToken != NULL ) ? pToken : "" ) ) ) ; + break; + /* Parsing network Registration status in CREG or CGREG or CEREG response. */ case CELLULAR_REG_POS_STAT: packetStatus = _parseRegStatusInRegStatusParsing( pContext, regType, pToken, pLibAtData ); break; + /* Parsing Location Area Code (LAC) or Tracking Area Code (TAC). */ case CELLULAR_REG_POS_LAC_TAC: - packetStatus = _parseLacTacInRegStatus( regType, pToken, pLibAtData ); + packetStatus = _parseLacTacInRegStatus( regType, pToken, pLibAtData, isUrcOrUrcModePSM /* allowEmpty= */ ); break; /* Parsing Cell ID. */ case CELLULAR_REG_POS_CELL_ID: - packetStatus = _parseCellIdInRegStatus( pToken, pLibAtData ); + packetStatus = _parseCellIdInRegStatus( pToken, pLibAtData, isUrcOrUrcModePSM /* allowEmpty= */ ); break; /* Parsing RAT Information. */ case CELLULAR_REG_POS_RAT: - packetStatus = _parseRatInfoInRegStatus( pToken, pLibAtData ); + packetStatus = _parseRatInfoInRegStatus( pToken, pLibAtData, isUrcOrUrcModePSM /* allowEmpty= */ ); break; /* Parsing Reject Type. */ case CELLULAR_REG_POS_REJ_TYPE: - packetStatus = _parseRejectTypeInRegStatus( regType, pToken, pLibAtData ); + packetStatus = _parseRejectTypeInRegStatus( regType, pToken, pLibAtData, isUrcOrUrcModePSM /* allowEmpty= */ ); break; /* Parsing the Reject Cause. */ case CELLULAR_REG_POS_REJ_CAUSE: - packetStatus = _parseRejectCauseInRegStatus( regType, pToken, pLibAtData ); + packetStatus = _parseRejectCauseInRegStatus( regType, pToken, pLibAtData, isUrcOrUrcModePSM /* allowEmpty= */ ); + break; + + /* Parsing the URC Active Time */ + case CELLULAR_REG_POS_URC_ACTIVE_TIME: + if( regType == CELLULAR_REG_TYPE_CGREG ) + { + /* Not currently supported */ + LogWarn( ( "CGREG Active Time Unsupported in Registration Status" ) ); + } + else if( regType == CELLULAR_REG_TYPE_CREG ) + { + LogError( ( "Unexpected Active Time in CREG Registration Status" ) ); + } + else + { + packetStatus = _parseActiveTimeInRegStatus( pToken, pLibAtData, true /* allowEmpty= */ ); + } + break; + + /* Parsing the URC Periodic TAU / RAU */ + case CELLULAR_REG_POS_URC_PERIODIC_TAU_RAU: + if( regType == CELLULAR_REG_TYPE_CGREG ) + { + /* Not currently supported */ + LogWarn( ( "CGREG Periodic RAU Unsupported in Registration Status" ) ); + } + else if( regType == CELLULAR_REG_TYPE_CREG ) + { + LogError( ( "Unexpected Periodic TAU / RAU in CREG Registration Status" ) ); + } + else + { + packetStatus = _parsePeriodicTauInRegStatus( pToken, pLibAtData, true /* allowEmpty= */ ); + } break; default: - LogDebug( ( "Unknown Parameter Position in Registration URC" ) ); + LogDebug( ( "Unknown Parameter Position in Registration URC: %hhu", i ) ); break; } @@ -541,7 +830,7 @@ CellularPktStatus_t _Cellular_ParseRegStatus( CellularContext_t * pContext, bool isUrc, CellularNetworkRegType_t regType ) { - uint8_t i = 0; + uint8_t i = 0, urcMode = CELLULAR_REG_URC_MODE_UNKNOWN; char * pRegStr = NULL, * pToken = NULL; cellularAtData_t * pLibAtData = NULL; CellularPktStatus_t packetStatus = CELLULAR_PKT_STATUS_OK; @@ -561,13 +850,16 @@ CellularPktStatus_t _Cellular_ParseRegStatus( CellularContext_t * pContext, { pLibAtData = &pContext->libAtData; - if( isUrc == true ) - { - i++; - } - pRegStr = pRegPayload; + // FUTURE: Remove once satisfied with Registration Status parsing, currently at error level to guarantee logging + LogError( ( "%s: '%s'", + ( ( regType == CELLULAR_REG_TYPE_CREG ) ? "CREG" : + ( ( regType == CELLULAR_REG_TYPE_CEREG ) ? "CEREG" : + ( ( regType == CELLULAR_REG_TYPE_CGREG ) ? "CGREG" : + ( ( regType == CELLULAR_REG_TYPE_UNKNOWN ) ? "unknown" : "" ) ) ) ), + ( ( pRegPayload != NULL ) ? pRegPayload : "" ) ) ); + atCoreStatus = Cellular_ATRemoveAllDoubleQuote( pRegStr ); if( atCoreStatus == CELLULAR_AT_SUCCESS ) @@ -580,6 +872,23 @@ CellularPktStatus_t _Cellular_ParseRegStatus( CellularContext_t * pContext, atCoreStatus = Cellular_ATGetNextTok( &pRegStr, &pToken ); } + if( atCoreStatus == CELLULAR_AT_SUCCESS ) + { + /* If not URC then first token will be the URC mode () */ + if( isUrc != true ) + { + atCoreStatus = _parseUrcModeInRegStatus( pToken, &urcMode ); + + if( atCoreStatus == CELLULAR_AT_SUCCESS ) + { + /* Get next token after URC mode */ + atCoreStatus = Cellular_ATGetNextTok( &pRegStr, &pToken ); + } + } + + i++; + } + if( atCoreStatus == CELLULAR_AT_SUCCESS ) { /* Backup the previous regStatus. */ @@ -589,8 +898,15 @@ CellularPktStatus_t _Cellular_ParseRegStatus( CellularContext_t * pContext, while( pToken != NULL ) { i++; - packetStatus = _regStatusSwitchParsingFunc( pContext, i, regType, - pToken, pLibAtData ); + packetStatus = _regStatusSwitchParsingFunc( pContext, i, regType, isUrc, + urcMode, pToken, pLibAtData ); + + if( packetStatus != CELLULAR_PKT_STATUS_OK ) + { + LogError( ("Failed to parse item %hhu: '%s', regType: %d, urcMode: %hhu, isURC: %s", + i, ( (pToken != NULL) ? pToken : "" ), regType, urcMode, + ( ( isUrc == true ) ? "true" : "false" ) ) ); + } /* Getting next token to parse. */ if( Cellular_ATGetNextTok( &pRegStr, &pToken ) != CELLULAR_AT_SUCCESS ) diff --git a/source/cellular_common_api.c b/source/cellular_common_api.c index 1afd55f5..b46cc269 100644 --- a/source/cellular_common_api.c +++ b/source/cellular_common_api.c @@ -334,6 +334,22 @@ CellularError_t Cellular_CommonATCommandRaw( CellularHandle_t cellularHandle, CellularATCommandResponseReceivedCallback_t responseReceivedCallback, void * pData, uint16_t dataLen ) +{ + return Cellular_CommonATCommandRawTimeout( cellularHandle, pATCommandPrefix, pATCommandPayload, atCommandType, + responseReceivedCallback, pData, dataLen, + CELLULAR_AT_COMMAND_RAW_TIMEOUT_MS ); +} + +/*-----------------------------------------------------------*/ + +CellularError_t Cellular_CommonATCommandRawTimeout( CellularHandle_t cellularHandle, + const char * pATCommandPrefix, + const char * pATCommandPayload, + CellularATCommandType_t atCommandType, + CellularATCommandResponseReceivedCallback_t responseReceivedCallback, + void * pData, + uint16_t dataLen, + uint32_t timeoutMS ) { CellularContext_t * pContext = ( CellularContext_t * ) cellularHandle; CellularError_t cellularStatus = CELLULAR_SUCCESS; @@ -363,7 +379,7 @@ CellularError_t Cellular_CommonATCommandRaw( CellularHandle_t cellularHandle, pktStatus = _Cellular_TimeoutAtcmdRequestWithCallback( pContext, atReqGetResult, - CELLULAR_AT_COMMAND_RAW_TIMEOUT_MS ); + timeoutMS ); cellularStatus = _Cellular_TranslatePktStatus( pktStatus ); } diff --git a/source/include/cellular_api.h b/source/include/cellular_api.h index fb615635..853dfe48 100644 --- a/source/include/cellular_api.h +++ b/source/include/cellular_api.h @@ -126,6 +126,28 @@ CellularError_t Cellular_RfOn( CellularHandle_t cellularHandle ); */ CellularError_t Cellular_RfOff( CellularHandle_t cellularHandle ); +/** + * @brief Turn off SIM card and turn off RF i.e. minimum functionality mode. + * + * @param[in] cellularHandle The opaque cellular context pointer created by Cellular_Init. + * + * @return CELLULAR_SUCCESS if the operation is successful, otherwise an error + * code indicating the cause of the error. + */ +CellularError_t Cellular_SimAndRfOff( CellularHandle_t cellularHandle ); + +/** + * @brief Get whether RF is on. + * + * @param[in] cellularHandle The opaque cellular context pointer created by Cellular_Init. + * @param[out] pRfFunctionality Out parameter to provide the RF functionality status. + * + * @return CELLULAR_SUCCESS if the operation is successful, otherwise an error + * code indicating the cause of the error. + */ +CellularError_t Cellular_GetRfFunctionality( CellularHandle_t cellularHandle, + CellularRfFunctionality_t * pRfFunctionality ); + /** * @brief Get SIM card status (activated/Pin set etc.). * @@ -421,10 +443,10 @@ CellularError_t Cellular_RegisterUrcGenericCallback( CellularHandle_t cellularHa void * pCallbackContext ); /** - * @brief Get current PSM settings. + * @brief Get current network PSM settings. * * @param[in] cellularHandle The opaque cellular context pointer created by Cellular_Init. - * @param[out] pPsmSettings Out parameter to provide the PSM settings. + * @param[out] pPsmSettings Out parameter to provide the PSM settings. NOTE: Values are NOT encoded * * @return CELLULAR_SUCCESS if the operation is successful, otherwise an error * code indicating the cause of the error. @@ -432,6 +454,18 @@ CellularError_t Cellular_RegisterUrcGenericCallback( CellularHandle_t cellularHa CellularError_t Cellular_GetPsmSettings( CellularHandle_t cellularHandle, CellularPsmSettings_t * pPsmSettings ); +/** + * @brief Get current requested PSM settings (network ultimately chooses PSM settings for Active time, etc). + * + * @param[in] cellularHandle The opaque cellular context pointer created by Cellular_Init. + * @param[out] pPsmSettings Out parameter to provide the PSM settings. NOTE: Values are encoded uint8 + * + * @return CELLULAR_SUCCESS if the operation is successful, otherwise an error + * code indicating the cause of the error. + */ +CellularError_t Cellular_GetRequestedPsmSettings( CellularHandle_t cellularHandle, + CellularPsmSettings_t * pPsmSettings ); + /** * @brief Set PSM settings. * @@ -524,6 +558,33 @@ CellularError_t Cellular_ATCommandRaw( CellularHandle_t cellularHandle, void * pData, uint16_t dataLen ); +/** + * @brief Send the raw AT command to the module with specified timeout. + * + * @param[in] cellularHandle The opaque cellular context pointer created by Cellular_Init. + * @param[in] pATCommandPrefix The AT command response prefix. NULL if the response + * has no prefix. + * @param[in] pATCommandPayload The AT command to send. It should be a NULL terminated + * string. + * @param[in] atCommandType Type of AT command. + * @param[in] responseReceivedCallback Callback to be invoked when a response for the + * command is received. + * @param[in] pData The pData pointer will be passed in responseReceivedCallback. + * @param[in] dataLen The dataLen value will be passed in responseReceivedCallback. + * @param[in] timeoutMS The timeout value to wait for the response from cellular modem. + * + * @return CELLULAR_SUCCESS if the operation is successful, otherwise an error + * code indicating the cause of the error. + */ +CellularError_t Cellular_ATCommandRawTimeout( CellularHandle_t cellularHandle, + const char * pATCommandPrefix, + const char * pATCommandPayload, + CellularATCommandType_t atCommandType, + CellularATCommandResponseReceivedCallback_t responseReceivedCallback, + void * pData, + uint16_t dataLen, + uint32_t timeoutMS ); + /** * @brief Create a socket. * diff --git a/source/include/cellular_types.h b/source/include/cellular_types.h index 46118b09..93e1a30e 100644 --- a/source/include/cellular_types.h +++ b/source/include/cellular_types.h @@ -592,6 +592,17 @@ typedef enum CellularPSMEnterMode CELLULAR_PSM_ENTER_MODE_IMMEDIATE /**< Enter PSM immediately after the RRC connection release is received. */ } CellularPSMEnterMode_t; +/** + * @ingroup cellular_datatypes_enums + * @brief Represents RF functionality modes. + */ +typedef enum CellularRfFunctionality +{ + CELLULAR_RF_FUNCTIONALITY_OFF, /**< RF functionality is off (minimum functionality). */ + CELLULAR_RF_FUNCTIONALITY_ON, /**< RF functionality is on (full functionality - RF and SIM). */ + CELLULAR_RF_FUNCTIONALITY_SIM_ONLY, /**< RF functionality is off but SIM interface enabled. */ +} CellularRfFunctionality_t; + /** * @ingroup cellular_datatypes_enums * @brief Represents Network Operator mode which places network operator specific requirements defined on top of 3GPP requirements. diff --git a/source/include/common/cellular_common_api.h b/source/include/common/cellular_common_api.h index 73405971..c9aa81f3 100644 --- a/source/include/common/cellular_common_api.h +++ b/source/include/common/cellular_common_api.h @@ -110,6 +110,19 @@ CellularError_t Cellular_CommonATCommandRaw( CellularHandle_t cellularHandle, void * pData, uint16_t dataLen ); +/** + * @brief This function is the common implementation of FreeRTOS Cellular Library API. + * Reference Cellular_ATCommandRawTimeout in cellular_api.h for definition. + */ +CellularError_t Cellular_CommonATCommandRawTimeout( CellularHandle_t cellularHandle, + const char * pATCommandPrefix, + const char * pATCommandPayload, + CellularATCommandType_t atCommandType, + CellularATCommandResponseReceivedCallback_t responseReceivedCallback, + void * pData, + uint16_t dataLen, + uint32_t timeoutMS ); + /** * @brief This function is the common implementation of FreeRTOS Cellular Library API. * Reference Cellular_CreateSocket in cellular_api.h for definition. diff --git a/source/include/private/cellular_common_internal.h b/source/include/private/cellular_common_internal.h index 86a91aa4..afcc6b79 100644 --- a/source/include/private/cellular_common_internal.h +++ b/source/include/private/cellular_common_internal.h @@ -95,6 +95,38 @@ typedef struct cellularAtData uint16_t lac; /**< Registered network operator Location Area Code. */ uint16_t rac; /**< Registered network operator Routing Area Code. */ uint16_t tac; /**< Registered network operator Tracking Area Code. */ + + /* + * Bits 5 to 1 represent the binary coded timer value + * Bits 6 to 8 define the timer value unit as follows: + * Bits + * 8 7 6 + * 0 0 0 value is incremented in multiples of 10 minutes + * 0 0 1 value is incremented in multiples of 1 hour + * 0 1 0 value is incremented in multiples of 10 hours + * 0 1 1 value is incremented in multiples of 2 seconds + * 1 0 0 value is incremented in multiples of 30 seconds + * 1 0 1 value is incremented in multiples of 1 minute + * + * e.g. "00001010" equals to 100 minutes. + * first uint8_t is used for PSM set, whole uint32_t is used for PSM get. + */ + uint32_t periodicTauValue; /**< TAU (T3412) value encoded as per spec (as shown above). 0xFFFFFFFF if unknown. */ + + /* + * Bits 5 to 1 represent the binary coded timer value. + * Bits 6 to 8 define the timer value unit as follows: + * Bits + * 8 7 6 + * 0 0 0 value is incremented in multiples of 2 seconds + * 0 0 1 value is incremented in multiples of 1 minute + * 0 1 0 value is incremented in multiples of decihours + * 1 1 1 value indicates that the timer is deactivated. + * + * "00001111" equals to 30 seconds. + * first uint8_t is used for PSM set, whole uint32_t is used for PSM get. + */ + uint32_t activeTimeValue; /**< Active Time (T3324) value encoded as per spec (as shown above). 0xFFFFFFFF if unknown. */ } cellularAtData_t; /**