1290 lines
48 KiB
C
1290 lines
48 KiB
C
/*
|
|
* FreeRTOS-Cellular-Interface v1.1.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
|
|
*/
|
|
|
|
/**
|
|
* @brief FreeRTOS Cellular Library common packet I/O functions to assemble packet from comm interface.
|
|
*/
|
|
|
|
#ifndef CELLULAR_DO_NOT_USE_CUSTOM_CONFIG
|
|
/* Include custom config file before other headers. */
|
|
#include "cellular_config.h"
|
|
#endif
|
|
#include "cellular_config_defaults.h"
|
|
|
|
/* Standard includes. */
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "cellular_platform.h"
|
|
#include "cellular_types.h"
|
|
#include "cellular_internal.h"
|
|
#include "cellular_pktio_internal.h"
|
|
#include "cellular_common_internal.h"
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#define PKTIO_EVT_MASK_STARTED ( 0x0001UL )
|
|
#define PKTIO_EVT_MASK_ABORT ( 0x0002UL )
|
|
#define PKTIO_EVT_MASK_ABORTED ( 0x0004UL )
|
|
#define PKTIO_EVT_MASK_RX_DATA ( 0x0008UL )
|
|
#define PKTIO_EVT_MASK_ALL_EVENTS \
|
|
( PKTIO_EVT_MASK_STARTED \
|
|
| PKTIO_EVT_MASK_ABORT \
|
|
| PKTIO_EVT_MASK_ABORTED \
|
|
| PKTIO_EVT_MASK_RX_DATA )
|
|
|
|
#define FREE_AT_RESPONSE_AND_SET_NULL( pResp ) { ( _Cellular_AtResponseFree( ( pResp ) ) ); ( ( pResp ) = NULL ); }
|
|
|
|
#define PKTIO_SHUTDOWN_WAIT_INTERVAL_MS ( 10U )
|
|
|
|
#ifdef CELLULAR_DO_NOT_USE_CUSTOM_CONFIG
|
|
#define LOOP_FOREVER() true
|
|
#endif
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void _saveData( char * pLine,
|
|
CellularATCommandResponse_t * pResp,
|
|
uint32_t dataLen );
|
|
static void _saveRawData( char * pLine,
|
|
CellularATCommandResponse_t * pResp,
|
|
uint32_t dataLen );
|
|
static void _saveATData( char * pLine,
|
|
CellularATCommandResponse_t * pResp );
|
|
static CellularPktStatus_t _processIntermediateResponse( char * pLine,
|
|
CellularATCommandResponse_t * pResp,
|
|
CellularATCommandType_t atType,
|
|
const char * pRespPrefix );
|
|
static CellularATCommandResponse_t * _Cellular_AtResponseNew( void );
|
|
static void _Cellular_AtResponseFree( CellularATCommandResponse_t * pResp );
|
|
static CellularPktStatus_t _Cellular_ProcessLine( const CellularContext_t * pContext,
|
|
char * pLine,
|
|
CellularATCommandResponse_t * pResp,
|
|
CellularATCommandType_t atType,
|
|
const char * pRespPrefix );
|
|
static bool urcTokenWoPrefix( const CellularContext_t * pContext,
|
|
const char * pLine );
|
|
static _atRespType_t _getMsgType( const CellularContext_t * pContext,
|
|
const char * pLine,
|
|
const char * pRespPrefix );
|
|
static CellularCommInterfaceError_t _Cellular_PktRxCallBack( void * pUserData,
|
|
CellularCommInterfaceHandle_t commInterfaceHandle );
|
|
static char * _handleLeftoverBuffer( CellularContext_t * pContext );
|
|
static char * _Cellular_ReadLine( CellularContext_t * pContext,
|
|
uint32_t * pBytesRead,
|
|
const CellularATCommandResponse_t * pAtResp );
|
|
static CellularPktStatus_t _handleData( char * pStartOfData,
|
|
CellularContext_t * pContext,
|
|
CellularATCommandResponse_t * pAtResp,
|
|
char ** ppLine,
|
|
uint32_t bytesRead,
|
|
uint32_t * pBytesLeft );
|
|
static CellularPktStatus_t _handleMsgType( CellularContext_t * pContext,
|
|
CellularATCommandResponse_t ** ppAtResp,
|
|
char * pLine );
|
|
static void _handleAllReceived( CellularContext_t * pContext,
|
|
CellularATCommandResponse_t ** ppAtResp,
|
|
char * pData,
|
|
uint32_t bytesInBuffer );
|
|
static uint32_t _handleRxDataEvent( CellularContext_t * pContext,
|
|
CellularATCommandResponse_t ** ppAtResp );
|
|
static void _pktioReadThread( void * pUserData );
|
|
static void _PktioInitProcessReadThreadStatus( CellularContext_t * pContext );
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static uint32_t _convertCharPtrDistance( const char * pEndPtr,
|
|
const char * pStartPtr )
|
|
{
|
|
int32_t ptrDistance = ( int32_t ) ( pEndPtr - pStartPtr );
|
|
|
|
return ( uint32_t ) ptrDistance;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void _saveData( char * pLine,
|
|
CellularATCommandResponse_t * pResp,
|
|
uint32_t dataLen )
|
|
{
|
|
CellularATCommandLine_t * pNew = NULL, * pTemp = NULL;
|
|
|
|
( void ) dataLen;
|
|
|
|
LogDebug( ( "_saveData : Save data %p with length %d", pLine, dataLen ) );
|
|
|
|
pNew = ( CellularATCommandLine_t * ) Platform_Malloc( sizeof( CellularATCommandLine_t ) );
|
|
configASSERT( ( pNew != NULL ) );
|
|
|
|
/* Reuse the pktio buffer instead of allocate. */
|
|
pNew->pLine = pLine;
|
|
pNew->pNext = NULL;
|
|
|
|
if( pResp->pItm == NULL )
|
|
{
|
|
pResp->pItm = pNew;
|
|
}
|
|
else
|
|
{
|
|
pTemp = pResp->pItm;
|
|
|
|
while( pTemp->pNext != NULL )
|
|
{
|
|
pTemp = pTemp->pNext;
|
|
}
|
|
|
|
pTemp->pNext = pNew;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void _saveRawData( char * pLine,
|
|
CellularATCommandResponse_t * pResp,
|
|
uint32_t dataLen )
|
|
{
|
|
LogDebug( ( "Save [%p] %d data to pResp", pLine, dataLen ) );
|
|
_saveData( pLine, pResp, dataLen );
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void _saveATData( char * pLine,
|
|
CellularATCommandResponse_t * pResp )
|
|
{
|
|
LogDebug( ( "Save [%s] %lu AT data to pResp", pLine, strlen( pLine ) ) );
|
|
_saveData( pLine, pResp, ( uint32_t ) ( strlen( pLine ) + 1U ) );
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static CellularPktStatus_t _processIntermediateResponse( char * pLine,
|
|
CellularATCommandResponse_t * pResp,
|
|
CellularATCommandType_t atType,
|
|
const char * pRespPrefix )
|
|
{
|
|
CellularPktStatus_t pkStatus = CELLULAR_PKT_STATUS_PENDING_DATA;
|
|
|
|
( void ) pRespPrefix;
|
|
|
|
switch( atType )
|
|
{
|
|
case CELLULAR_AT_WO_PREFIX:
|
|
|
|
if( pResp->pItm == NULL )
|
|
{
|
|
_saveATData( pLine, pResp );
|
|
}
|
|
else
|
|
{
|
|
/* We already have an intermediate response. */
|
|
pkStatus = CELLULAR_PKT_STATUS_INVALID_DATA;
|
|
LogError( ( "CELLULAR_AT_WO_PREFIX AT process ERROR: %s, status: %d ", pLine, pkStatus ) );
|
|
}
|
|
|
|
break;
|
|
|
|
case CELLULAR_AT_WITH_PREFIX:
|
|
|
|
if( pResp->pItm == NULL )
|
|
{
|
|
/* The removed code which demonstrate the existence of the prefix has been done in
|
|
* function _getMsgType(), so the failure condition here won't be touched.
|
|
*/
|
|
_saveATData( pLine, pResp );
|
|
}
|
|
else
|
|
{
|
|
/* We already have an intermediate response. */
|
|
pkStatus = CELLULAR_PKT_STATUS_INVALID_DATA;
|
|
LogError( ( "CELLULAR_AT_WITH_PREFIX AT process ERROR: %s, status: %d ", pLine, pkStatus ) );
|
|
}
|
|
|
|
break;
|
|
|
|
case CELLULAR_AT_MULTI_WITH_PREFIX:
|
|
|
|
/* The removed code which demonstrate the existence of the prefix has been done in
|
|
* function _getMsgType(), so the failure condition here won't be touched.
|
|
*/
|
|
_saveATData( pLine, pResp );
|
|
|
|
break;
|
|
|
|
case CELLULAR_AT_MULTI_WO_PREFIX:
|
|
_saveATData( pLine, pResp );
|
|
break;
|
|
|
|
case CELLULAR_AT_MULTI_DATA_WO_PREFIX:
|
|
default:
|
|
_saveATData( pLine, pResp );
|
|
pkStatus = CELLULAR_PKT_STATUS_PENDING_BUFFER;
|
|
break;
|
|
}
|
|
|
|
return pkStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static CellularATCommandResponse_t * _Cellular_AtResponseNew( void )
|
|
{
|
|
CellularATCommandResponse_t * pNew = NULL;
|
|
|
|
pNew = ( CellularATCommandResponse_t * ) Platform_Malloc( sizeof( CellularATCommandResponse_t ) );
|
|
configASSERT( ( pNew != NULL ) );
|
|
|
|
( void ) memset( ( void * ) pNew, 0, sizeof( CellularATCommandResponse_t ) );
|
|
|
|
return pNew;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* Returns a pointer to the end of the next line
|
|
* special-cases the "> " SMS prompt.
|
|
*
|
|
* Returns NULL if there is no complete line.
|
|
*/
|
|
static void _Cellular_AtResponseFree( CellularATCommandResponse_t * pResp )
|
|
{
|
|
CellularATCommandLine_t * pCurrLine = NULL;
|
|
CellularATCommandLine_t * pToFree = NULL;
|
|
|
|
if( pResp != NULL )
|
|
{
|
|
pCurrLine = pResp->pItm;
|
|
|
|
while( pCurrLine != NULL )
|
|
{
|
|
pToFree = pCurrLine;
|
|
pCurrLine = pCurrLine->pNext;
|
|
|
|
/* Ruese the pktiobuffer. No need to free pToFree->pLine here. */
|
|
Platform_Free( pToFree );
|
|
}
|
|
|
|
Platform_Free( pResp );
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static CellularPktStatus_t _Cellular_ProcessLine( const CellularContext_t * pContext,
|
|
char * pLine,
|
|
CellularATCommandResponse_t * pResp,
|
|
CellularATCommandType_t atType,
|
|
const char * pRespPrefix )
|
|
{
|
|
CellularPktStatus_t pkStatus = CELLULAR_PKT_STATUS_FAILURE;
|
|
bool result = false;
|
|
const char * const * pTokenSuccessTable = NULL;
|
|
const char * const * pTokenErrorTable = NULL;
|
|
const char * const * pTokenExtraTable = NULL;
|
|
uint32_t tokenSuccessTableSize = 0;
|
|
uint32_t tokenErrorTableSize = 0;
|
|
uint32_t tokenExtraTableSize = 0;
|
|
|
|
if( ( pContext->tokenTable.pCellularSrcTokenErrorTable != NULL ) &&
|
|
( pContext->tokenTable.pCellularSrcTokenSuccessTable != NULL ) )
|
|
{
|
|
pTokenSuccessTable = pContext->tokenTable.pCellularSrcTokenSuccessTable;
|
|
tokenSuccessTableSize = pContext->tokenTable.cellularSrcTokenSuccessTableSize;
|
|
pTokenErrorTable = pContext->tokenTable.pCellularSrcTokenErrorTable;
|
|
tokenErrorTableSize = pContext->tokenTable.cellularSrcTokenErrorTableSize;
|
|
pTokenExtraTable = pContext->tokenTable.pCellularSrcExtraTokenSuccessTable;
|
|
tokenExtraTableSize = pContext->tokenTable.cellularSrcExtraTokenSuccessTableSize;
|
|
|
|
/* pResp has been checked while allocating memory, so we don't
|
|
* need to demonstrate it here.
|
|
*/
|
|
( void ) Cellular_ATcheckErrorCode( pLine, pTokenExtraTable,
|
|
tokenExtraTableSize, &result );
|
|
|
|
if( result == true )
|
|
{
|
|
pResp->status = true;
|
|
pkStatus = CELLULAR_PKT_STATUS_OK;
|
|
LogDebug( ( "Final AT response is SUCCESS [%s] in extra table", pLine ) );
|
|
}
|
|
else
|
|
{
|
|
( void ) Cellular_ATcheckErrorCode( pLine, pTokenSuccessTable,
|
|
tokenSuccessTableSize, &result );
|
|
|
|
if( result == true )
|
|
{
|
|
pResp->status = true;
|
|
pkStatus = CELLULAR_PKT_STATUS_OK;
|
|
LogDebug( ( "Final AT response is SUCCESS [%s]", pLine ) );
|
|
}
|
|
}
|
|
|
|
if( result != true )
|
|
{
|
|
( void ) Cellular_ATcheckErrorCode( pLine, pTokenErrorTable,
|
|
tokenErrorTableSize, &result );
|
|
|
|
if( result == true )
|
|
{
|
|
pResp->status = false;
|
|
pkStatus = CELLULAR_PKT_STATUS_OK;
|
|
}
|
|
else
|
|
{
|
|
pkStatus = _processIntermediateResponse( pLine, pResp, atType, pRespPrefix );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ( result == true ) && ( pResp->status == false ) )
|
|
{
|
|
LogError( ( "Modem return ERROR: line %s, cmd : %s, respPrefix %s, status: %d",
|
|
( pContext->pCurrentCmd != NULL ? pContext->pCurrentCmd : "NULL" ),
|
|
pLine,
|
|
( pRespPrefix != NULL ? pRespPrefix : "NULL" ),
|
|
pkStatus ) );
|
|
}
|
|
|
|
return pkStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static bool urcTokenWoPrefix( const CellularContext_t * pContext,
|
|
const char * pLine )
|
|
{
|
|
bool ret = false;
|
|
uint32_t i = 0;
|
|
uint32_t urcTokenTableSize = pContext->tokenTable.cellularUrcTokenWoPrefixTableSize;
|
|
const char * const * const pUrcTokenTable = pContext->tokenTable.pCellularUrcTokenWoPrefixTable;
|
|
|
|
for( i = 0; i < urcTokenTableSize; i++ )
|
|
{
|
|
if( strcmp( pLine, pUrcTokenTable[ i ] ) == 0 )
|
|
{
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static _atRespType_t _getMsgType( const CellularContext_t * pContext,
|
|
const char * pLine,
|
|
const char * pRespPrefix )
|
|
{
|
|
_atRespType_t atRespType = AT_UNDEFINED;
|
|
CellularATError_t atStatus = CELLULAR_AT_SUCCESS;
|
|
bool inputWithPrefix = false;
|
|
bool inputWithSrcPrefix = false;
|
|
|
|
if( pContext->tokenTable.pCellularUrcTokenWoPrefixTable == NULL )
|
|
{
|
|
atStatus = CELLULAR_AT_ERROR;
|
|
atRespType = AT_UNDEFINED;
|
|
}
|
|
else if( urcTokenWoPrefix( pContext, pLine ) == true )
|
|
{
|
|
atRespType = AT_UNSOLICITED;
|
|
}
|
|
else
|
|
{
|
|
/* Check if prefix exist in pLine. */
|
|
( void ) Cellular_ATIsPrefixPresent( pLine, &inputWithPrefix );
|
|
|
|
if( ( inputWithPrefix == true ) && ( pRespPrefix != NULL ) )
|
|
{
|
|
/* Check if SRC prefix exist in pLine. */
|
|
atStatus = Cellular_ATStrStartWith( pLine, pRespPrefix, &inputWithSrcPrefix );
|
|
}
|
|
}
|
|
|
|
if( ( atStatus == CELLULAR_AT_SUCCESS ) && ( atRespType == AT_UNDEFINED ) )
|
|
{
|
|
if( inputWithPrefix == true )
|
|
{
|
|
if( ( pContext->PktioAtCmdType != CELLULAR_AT_NO_COMMAND ) && ( inputWithSrcPrefix == true ) )
|
|
{
|
|
atRespType = AT_SOLICITED;
|
|
}
|
|
else
|
|
{
|
|
atRespType = AT_UNSOLICITED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ( ( pContext->PktioAtCmdType != CELLULAR_AT_NO_COMMAND ) && ( pRespPrefix == NULL ) ) ||
|
|
( pContext->PktioAtCmdType == CELLULAR_AT_MULTI_DATA_WO_PREFIX ) ||
|
|
( pContext->PktioAtCmdType == CELLULAR_AT_WITH_PREFIX ) ||
|
|
( pContext->PktioAtCmdType == CELLULAR_AT_MULTI_WITH_PREFIX ) )
|
|
{
|
|
atRespType = AT_SOLICITED;
|
|
}
|
|
}
|
|
}
|
|
|
|
return atRespType;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static CellularCommInterfaceError_t _Cellular_PktRxCallBack( void * pUserData,
|
|
CellularCommInterfaceHandle_t commInterfaceHandle )
|
|
{
|
|
const CellularContext_t * pContext = ( CellularContext_t * ) pUserData;
|
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE, xResult = pdFALSE;
|
|
CellularCommInterfaceError_t retComm = IOT_COMM_INTERFACE_SUCCESS;
|
|
|
|
( void ) commInterfaceHandle; /* Comm if is not used in this function. */
|
|
|
|
/* The context of this function is a ISR. */
|
|
if( pContext->pPktioCommEvent == ( uintptr_t ) ( uintptr_t * ) NULL )
|
|
{
|
|
retComm = IOT_COMM_INTERFACE_BAD_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
/* It's platform-dependent declaration. */
|
|
/* coverity[misra_c_2012_directive_4_6_violation] */
|
|
xResult = ( BaseType_t ) PlatformEventGroup_SetBitsFromISR( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent,
|
|
( EventBits_t ) PKTIO_EVT_MASK_RX_DATA,
|
|
&xHigherPriorityTaskWoken );
|
|
|
|
if( xResult == pdPASS )
|
|
{
|
|
if( xHigherPriorityTaskWoken == pdTRUE )
|
|
{
|
|
retComm = IOT_COMM_INTERFACE_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
retComm = IOT_COMM_INTERFACE_BUSY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
retComm = IOT_COMM_INTERFACE_FAILURE;
|
|
}
|
|
}
|
|
|
|
return retComm;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static char * _handleLeftoverBuffer( CellularContext_t * pContext )
|
|
{
|
|
char * pRead = NULL; /* Pointer to first empty space in pContext->pktioReadBuf. */
|
|
|
|
/* Move the leftover data or AT command response to the start of buffer.
|
|
* Set the pRead pointer to the empty buffer space. */
|
|
|
|
LogDebug( ( "moved the partial line/data from %p to %p %d",
|
|
pContext->pPktioReadPtr, pContext->pktioReadBuf, pContext->partialDataRcvdLen ) );
|
|
|
|
( void ) memmove( pContext->pktioReadBuf, pContext->pPktioReadPtr, pContext->partialDataRcvdLen );
|
|
pContext->pktioReadBuf[ pContext->partialDataRcvdLen ] = '\0';
|
|
|
|
pRead = &pContext->pktioReadBuf[ pContext->partialDataRcvdLen ];
|
|
|
|
pContext->pPktioReadPtr = pContext->pktioReadBuf;
|
|
|
|
return pRead;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/* pBytesRead : bytes read from comm interface. */
|
|
/* partialData : leftover bytes in the pktioreadbuf. Not enougth to be a command. */
|
|
static char * _Cellular_ReadLine( CellularContext_t * pContext,
|
|
uint32_t * pBytesRead,
|
|
const CellularATCommandResponse_t * pAtResp )
|
|
{
|
|
char * pAtBuf = NULL; /* The returned start of data. */
|
|
char * pRead = NULL; /* pRead is the first empty ptr in the Buffer for comm intf to read. */
|
|
uint32_t bytesRead = 0;
|
|
uint32_t partialDataRead = pContext->partialDataRcvdLen;
|
|
int32_t bufferEmptyLength = ( int32_t ) PKTIO_READ_BUFFER_SIZE;
|
|
|
|
pAtBuf = pContext->pktioReadBuf;
|
|
pRead = pContext->pktioReadBuf;
|
|
|
|
/* pContext->pPktioReadPtr is valid data start pointer.
|
|
* pContext->partialDataRcvdLen is the valid data length need to be handled.
|
|
* if pContext->pPktioReadPtr is NULL, valid data start from pContext->pktioReadBuf.
|
|
* pAtResp equals NULL indicate that no data is buffered in AT command response and
|
|
* data before pPktioReadPtr is invalid data can be recycled. */
|
|
if( ( pContext->pPktioReadPtr != NULL ) && ( pContext->pPktioReadPtr != pContext->pktioReadBuf ) &&
|
|
( pContext->partialDataRcvdLen != 0U ) && ( pAtResp == NULL ) )
|
|
{
|
|
pRead = _handleLeftoverBuffer( pContext );
|
|
bufferEmptyLength = ( ( int32_t ) PKTIO_READ_BUFFER_SIZE - ( int32_t ) pContext->partialDataRcvdLen );
|
|
}
|
|
else
|
|
{
|
|
if( pContext->pPktioReadPtr != NULL )
|
|
{
|
|
/* There are still valid data before pPktioReadPtr. */
|
|
pRead = &pContext->pPktioReadPtr[ pContext->partialDataRcvdLen ];
|
|
pAtBuf = pContext->pPktioReadPtr;
|
|
bufferEmptyLength = ( ( int32_t ) PKTIO_READ_BUFFER_SIZE -
|
|
( int32_t ) pContext->partialDataRcvdLen - ( int32_t ) _convertCharPtrDistance( pContext->pPktioReadPtr, pContext->pktioReadBuf ) );
|
|
}
|
|
else
|
|
{
|
|
/* There are valid data need to be handled with length pContext->partialDataRcvdLen. */
|
|
pRead = &pContext->pktioReadBuf[ pContext->partialDataRcvdLen ];
|
|
pAtBuf = pContext->pktioReadBuf;
|
|
bufferEmptyLength = ( ( int32_t ) PKTIO_READ_BUFFER_SIZE - ( int32_t ) pContext->partialDataRcvdLen );
|
|
}
|
|
}
|
|
|
|
if( bufferEmptyLength > 0 )
|
|
{
|
|
( void ) pContext->pCommIntf->recv( pContext->hPktioCommIntf, ( uint8_t * ) pRead,
|
|
bufferEmptyLength,
|
|
CELLULAR_COMM_IF_RECV_TIMEOUT_MS, &bytesRead );
|
|
|
|
if( bytesRead > 0U )
|
|
{
|
|
/* Add a NULL after the bytesRead. This is required for further processing. */
|
|
pRead[ bytesRead ] = '\0';
|
|
|
|
LogDebug( ( "AT Read %d bytes, data[%p]", bytesRead, pRead ) );
|
|
/* Set the pBytesRead only when actual bytes read from comm interface. */
|
|
*pBytesRead = bytesRead + partialDataRead;
|
|
|
|
/* Clean the partial data and read pointer. */
|
|
pContext->partialDataRcvdLen = 0;
|
|
}
|
|
else
|
|
{
|
|
pAtBuf = NULL;
|
|
*pBytesRead = 0U;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogError( ( "No empty space from comm if to handle incoming data, reset all parameter for next incoming data." ) );
|
|
*pBytesRead = 0;
|
|
pContext->partialDataRcvdLen = 0;
|
|
pContext->pPktioReadPtr = NULL;
|
|
}
|
|
|
|
return pAtBuf;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static CellularPktStatus_t _handleData( char * pStartOfData,
|
|
CellularContext_t * pContext,
|
|
CellularATCommandResponse_t * pAtResp,
|
|
char ** ppLine,
|
|
uint32_t bytesRead,
|
|
uint32_t * pBytesLeft )
|
|
{
|
|
/* Calculate the size of data received so far. */
|
|
uint32_t bytesBeforeData = _convertCharPtrDistance( pStartOfData, *ppLine );
|
|
CellularPktStatus_t pkStatus = CELLULAR_PKT_STATUS_OK;
|
|
uint32_t bytesDataAndLeft = 0;
|
|
|
|
/* Bytes before pStartOfData is not data, skip the bytes received. */
|
|
/* bytesRead = bytesBeforeData( data prefix ) + bytesData + bytesLeft( other AT command response ). */
|
|
bytesDataAndLeft = bytesRead - bytesBeforeData;
|
|
|
|
if( bytesDataAndLeft >= pContext->dataLength )
|
|
{
|
|
/* Add data to the response linked list. */
|
|
_saveRawData( pStartOfData, pAtResp, pContext->dataLength );
|
|
|
|
/* Advance pLine to a point after data. */
|
|
*ppLine = &pStartOfData[ pContext->dataLength ];
|
|
|
|
/* There are more bytes after the data. */
|
|
*pBytesLeft = ( bytesDataAndLeft - pContext->dataLength );
|
|
|
|
LogDebug( ( "_handleData : read buffer buffer %p start %p prefix %d left %d, read total %d",
|
|
pContext->pktioReadBuf,
|
|
pStartOfData,
|
|
bytesBeforeData,
|
|
*pBytesLeft,
|
|
bytesRead ) );
|
|
|
|
/* reset the data related variables. */
|
|
pContext->dataLength = 0U;
|
|
|
|
/* Set the pPktioReadPtr to indicate data already handled. */
|
|
pContext->pPktioReadPtr = *ppLine;
|
|
pContext->partialDataRcvdLen = *pBytesLeft;
|
|
}
|
|
else
|
|
{
|
|
/* The data received is partial. Store the start of data in read pointer. */
|
|
pContext->pPktioReadPtr = pStartOfData;
|
|
pContext->partialDataRcvdLen = bytesDataAndLeft;
|
|
pkStatus = CELLULAR_PKT_STATUS_PENDING_BUFFER;
|
|
}
|
|
|
|
return pkStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static CellularPktStatus_t _handleMsgType( CellularContext_t * pContext,
|
|
CellularATCommandResponse_t ** ppAtResp,
|
|
char * pLine )
|
|
{
|
|
CellularPktStatus_t pkStatus = CELLULAR_PKT_STATUS_OK;
|
|
|
|
if( pContext->recvdMsgType == AT_UNSOLICITED )
|
|
{
|
|
if( pContext->pPktioHandlepktCB != NULL )
|
|
{
|
|
( void ) pContext->pPktioHandlepktCB( pContext, AT_UNSOLICITED, pLine );
|
|
}
|
|
}
|
|
else if( pContext->recvdMsgType == AT_SOLICITED )
|
|
{
|
|
if( *ppAtResp == NULL )
|
|
{
|
|
*ppAtResp = _Cellular_AtResponseNew();
|
|
LogDebug( ( "Allocate at response %p", ( void * ) *ppAtResp ) );
|
|
}
|
|
|
|
LogDebug( ( "AT solicited Resp[%s]", pLine ) );
|
|
|
|
/* Process Line will store the Line data in AT response. */
|
|
pkStatus = _Cellular_ProcessLine( pContext, pLine, *ppAtResp, pContext->PktioAtCmdType, pContext->pRespPrefix );
|
|
|
|
if( pkStatus == CELLULAR_PKT_STATUS_OK )
|
|
{
|
|
if( pContext->pPktioHandlepktCB != NULL )
|
|
{
|
|
( void ) pContext->pPktioHandlepktCB( pContext, AT_SOLICITED, *ppAtResp );
|
|
}
|
|
|
|
FREE_AT_RESPONSE_AND_SET_NULL( *ppAtResp );
|
|
}
|
|
else if( pkStatus == CELLULAR_PKT_STATUS_PENDING_BUFFER )
|
|
{
|
|
/* Check data prefix first then store the data if this command has data response. */
|
|
}
|
|
else
|
|
{
|
|
if( pkStatus != CELLULAR_PKT_STATUS_PENDING_DATA )
|
|
{
|
|
( void ) memset( pContext->pktioReadBuf, 0, PKTIO_READ_BUFFER_SIZE + 1U );
|
|
pContext->pPktioReadPtr = NULL;
|
|
FREE_AT_RESPONSE_AND_SET_NULL( *ppAtResp );
|
|
/* pContext->pCurrentCmd is not NULL since it is a solicited response. */
|
|
LogError( ( "processLine ERROR, cleaning up! Current command %s", pContext->pCurrentCmd ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogError( ( "recvdMsgType is AT_UNDEFINED for Message: %s, cmd %s",
|
|
pLine,
|
|
( pContext->pCurrentCmd != NULL ? pContext->pCurrentCmd : "NULL" ) ) );
|
|
( void ) memset( pContext->pktioReadBuf, 0, PKTIO_READ_BUFFER_SIZE + 1U );
|
|
pContext->pPktioReadPtr = NULL;
|
|
pContext->partialDataRcvdLen = 0;
|
|
FREE_AT_RESPONSE_AND_SET_NULL( *ppAtResp );
|
|
pkStatus = CELLULAR_PKT_STATUS_BAD_PARAM;
|
|
}
|
|
|
|
return pkStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static bool _findLineInStream( CellularContext_t * pContext,
|
|
char * pLine,
|
|
uint32_t bytesRead )
|
|
{
|
|
bool keepProcess = true;
|
|
char * pTempLine = pLine;
|
|
uint32_t i = 0;
|
|
|
|
/* Handle the complete line here. GetMsgType needs a complete Line or longer then maximum prefix line. */
|
|
for( i = 0; i < bytesRead; i++ )
|
|
{
|
|
if( ( pTempLine[ i ] == '\0' ) || ( pTempLine[ i ] == '\r' ) || ( pTempLine[ i ] == '\n' ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* A complete Line is found. */
|
|
if( i < bytesRead )
|
|
{
|
|
pTempLine[ i ] = '\0';
|
|
}
|
|
else
|
|
{
|
|
LogDebug( ( "%p is not a complete line", pTempLine ) );
|
|
pContext->pPktioReadPtr = pTempLine;
|
|
pContext->partialDataRcvdLen = bytesRead;
|
|
keepProcess = false;
|
|
}
|
|
|
|
return keepProcess;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static bool _preprocessLine( CellularContext_t * pContext,
|
|
char * pLine,
|
|
uint32_t * pBytesRead,
|
|
char ** ppStartOfData )
|
|
{
|
|
char * pTempLine = pLine;
|
|
bool keepProcess = true;
|
|
CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK;
|
|
|
|
/* The line only has change line. */
|
|
if( *pBytesRead <= 0U )
|
|
{
|
|
pContext->pPktioReadPtr = pTempLine;
|
|
pContext->partialDataRcvdLen = 0;
|
|
keepProcess = false;
|
|
}
|
|
else
|
|
{
|
|
if( pContext->pktDataSendPrefixCB != NULL )
|
|
{
|
|
/* Check if the AT command response is the data send prefix.
|
|
* Data send prefix is an SRC success token for data send AT commmand.
|
|
* It is used to indicate modem can receive data now. */
|
|
/* This function may fix the data stream if the data send prefix is not a line. */
|
|
pktStatus = pContext->pktDataSendPrefixCB( pContext->pDataSendPrefixCBContext, pTempLine, pBytesRead );
|
|
|
|
if( pktStatus != CELLULAR_PKT_STATUS_OK )
|
|
{
|
|
LogError( ( "pktDataSendPrefixCB returns error %d", pktStatus ) );
|
|
keepProcess = false;
|
|
}
|
|
}
|
|
else if( pContext->pktDataPrefixCB != NULL )
|
|
{
|
|
/* Check if the AT command response is the data receive prefix.
|
|
* Data receive prefix is an SRC success token for data receive AT commnad.
|
|
* It is used to indicate modem will start to send data. Don't parse the content. */
|
|
/* ppStartOfData and pDataLength are not set if this function failed. */
|
|
|
|
/* This function may fix the data stream if the AT response and data
|
|
* received are in the same line. */
|
|
pktStatus = pContext->pktDataPrefixCB( pContext->pDataPrefixCBContext,
|
|
pTempLine, *pBytesRead,
|
|
ppStartOfData, &pContext->dataLength );
|
|
|
|
if( pktStatus == CELLULAR_PKT_STATUS_OK )
|
|
{
|
|
/* These members filled by user callback function and need to be demonstrated. */
|
|
if( pContext->dataLength > 0U )
|
|
{
|
|
configASSERT( ppStartOfData != NULL );
|
|
}
|
|
}
|
|
else if( pktStatus == CELLULAR_PKT_STATUS_SIZE_MISMATCH )
|
|
{
|
|
/* The modem driver is waiting for more data to decide. */
|
|
LogDebug( ( "%p is not a complete line", pTempLine ) );
|
|
pContext->pPktioReadPtr = pTempLine;
|
|
pContext->partialDataRcvdLen = *pBytesRead;
|
|
keepProcess = false;
|
|
}
|
|
else
|
|
{
|
|
LogError( ( "pktDataPrefixCB returns error %d", pktStatus ) );
|
|
keepProcess = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This is the case AT command don't need data send or data receive prefix. */
|
|
/* MISRA empty else. */
|
|
}
|
|
|
|
if( keepProcess == true )
|
|
{
|
|
keepProcess = _findLineInStream( pContext, pTempLine, *pBytesRead );
|
|
}
|
|
}
|
|
|
|
return keepProcess;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static bool _handleDataResult( CellularContext_t * pContext,
|
|
CellularATCommandResponse_t * const * ppAtResp,
|
|
char * pStartOfData,
|
|
char ** ppLine,
|
|
uint32_t * pBytesRead )
|
|
{
|
|
uint32_t bytesLeft = 0;
|
|
bool keepProcess = true;
|
|
|
|
/* The input line is a data recv command. Handle the data buffer. */
|
|
( void ) _handleData( pStartOfData, pContext, *ppAtResp, ppLine, *pBytesRead, &bytesLeft );
|
|
|
|
/* pktStatus will never be CELLULAR_PKT_STATUS_PENDING_BUFFER from _handleData(). */
|
|
if( bytesLeft == 0U )
|
|
{
|
|
LogDebug( ( "Complete Data received" ) );
|
|
keepProcess = false;
|
|
}
|
|
else
|
|
{
|
|
*pBytesRead = bytesLeft;
|
|
LogDebug( ( "_handleData okay, keep processing %u bytes %p", bytesLeft, *ppLine ) );
|
|
}
|
|
|
|
return keepProcess;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static bool _getNextLine( CellularContext_t * pContext,
|
|
char ** ppLine,
|
|
uint32_t * pBytesRead,
|
|
CellularPktStatus_t pktStatus )
|
|
{
|
|
uint32_t stringLength = 0;
|
|
bool keepProcess = true;
|
|
|
|
/* Find other responses or urcs which need to be processed in this read buffer. */
|
|
stringLength = ( uint32_t ) strnlen( *ppLine, *pBytesRead );
|
|
|
|
/* Advanced 1 bytes to read next Line. */
|
|
*ppLine = &( ( *ppLine )[ ( stringLength + 1U ) ] );
|
|
*pBytesRead = *pBytesRead - ( stringLength + 1U );
|
|
pContext->pPktioReadPtr = *ppLine;
|
|
pContext->partialDataRcvdLen = *pBytesRead;
|
|
|
|
if( ( pktStatus == CELLULAR_PKT_STATUS_OK ) && ( pContext->recvdMsgType == AT_SOLICITED ) )
|
|
{
|
|
/* Garbage collection. */
|
|
LogDebug( ( "Garbage collection" ) );
|
|
( void ) memmove( pContext->pktioReadBuf, *ppLine, *pBytesRead );
|
|
*ppLine = pContext->pktioReadBuf;
|
|
pContext->pPktioReadPtr = pContext->pktioReadBuf;
|
|
pContext->partialDataRcvdLen = *pBytesRead;
|
|
}
|
|
|
|
return keepProcess;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/* This function handle message in line( string terminated with \r or \n ). */
|
|
static void _handleAllReceived( CellularContext_t * pContext,
|
|
CellularATCommandResponse_t ** ppAtResp,
|
|
char * pData,
|
|
uint32_t bytesInBuffer )
|
|
{
|
|
CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK;
|
|
char * pStartOfData = NULL, * pTempLine = pData;
|
|
uint32_t bytesRead = bytesInBuffer;
|
|
bool keepProcess = true;
|
|
|
|
while( keepProcess == true )
|
|
{
|
|
/* Pktio is reading command. Skip over the change line. And the reason
|
|
* we don't consider the variable bytesInBuffer is because that the
|
|
* input variable bytesInBuffer is bounded by the caller already.
|
|
*/
|
|
while( ( bytesRead > 0U ) && ( ( *pTempLine == '\r' ) || ( *pTempLine == '\n' ) ) )
|
|
{
|
|
pTempLine++;
|
|
bytesRead = bytesRead - 1U;
|
|
}
|
|
|
|
/* Preprocess line. */
|
|
keepProcess = _preprocessLine( pContext, pTempLine, &bytesRead, &pStartOfData );
|
|
|
|
if( keepProcess == true )
|
|
{
|
|
/* A complete Line received. Get the message type. */
|
|
pContext->recvdMsgType = _getMsgType( pContext, pTempLine, pContext->pRespPrefix );
|
|
|
|
/* Handle the message according the received message type. */
|
|
pktStatus = _handleMsgType( pContext, ppAtResp, pTempLine );
|
|
|
|
if( pktStatus == CELLULAR_PKT_STATUS_PENDING_BUFFER )
|
|
{
|
|
/* The input line is a data recv command. Handle the data buffer. */
|
|
if( pContext->dataLength != 0U )
|
|
{
|
|
keepProcess = _handleDataResult( pContext, ppAtResp, pStartOfData, &pTempLine, &bytesRead );
|
|
}
|
|
else
|
|
{
|
|
keepProcess = _getNextLine( pContext, &pTempLine, &bytesRead, pktStatus );
|
|
}
|
|
}
|
|
else if( ( pktStatus == CELLULAR_PKT_STATUS_OK ) || ( pktStatus == CELLULAR_PKT_STATUS_PENDING_DATA ) )
|
|
{
|
|
/* Process AT reponse success. Get the next Line. */
|
|
keepProcess = _getNextLine( pContext, &pTempLine, &bytesRead, pktStatus );
|
|
}
|
|
else
|
|
{
|
|
LogDebug( ( "_handleMsgType failed %d", pktStatus ) );
|
|
keepProcess = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static uint32_t _handleRxDataEvent( CellularContext_t * pContext,
|
|
CellularATCommandResponse_t ** ppAtResp )
|
|
{
|
|
char * pLine = NULL;
|
|
uint32_t bytesRead = 0;
|
|
uint32_t bytesLeft = 0;
|
|
|
|
/* Return the first line, may be more lines in buffer. */
|
|
/* Start from pLine there are bytesRead bytes. */
|
|
pLine = _Cellular_ReadLine( pContext, &bytesRead, *ppAtResp );
|
|
|
|
if( bytesRead > 0U )
|
|
{
|
|
if( pContext->dataLength != 0U )
|
|
{
|
|
( void ) _handleData( pLine, pContext, *ppAtResp, &pLine, bytesRead, &bytesLeft );
|
|
}
|
|
else
|
|
{
|
|
bytesLeft = bytesRead;
|
|
}
|
|
|
|
/* If bytesRead in _Cellular_ReadLine is all for data, don't parse the AT command response. */
|
|
if( bytesLeft > 0U )
|
|
{
|
|
/* Add the null terminated char to the end of pLine. */
|
|
pLine[ bytesLeft ] = '\0';
|
|
_handleAllReceived( pContext, ppAtResp, pLine, bytesLeft );
|
|
}
|
|
}
|
|
|
|
/* Return the bytes read from comm interface to do more reading. */
|
|
return bytesRead;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
static void _pktioReadThread( void * pUserData )
|
|
{
|
|
CellularContext_t * pContext = ( CellularContext_t * ) pUserData;
|
|
CellularATCommandResponse_t * pAtResp = NULL;
|
|
PlatformEventGroup_EventBits uxBits = 0;
|
|
uint32_t bytesRead = 0U;
|
|
|
|
/* Open main communication port. */
|
|
if( ( pContext->pCommIntf != NULL ) &&
|
|
( pContext->pCommIntf->open( _Cellular_PktRxCallBack, ( void * ) pContext,
|
|
&pContext->hPktioCommIntf ) == IOT_COMM_INTERFACE_SUCCESS ) )
|
|
{
|
|
/* Send thread started event. */
|
|
/* It's platform-dependent declaration. */
|
|
/* coverity[misra_c_2012_directive_4_6_violation] */
|
|
( void ) PlatformEventGroup_SetBits( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent, ( EventBits_t ) PKTIO_EVT_MASK_STARTED );
|
|
|
|
do
|
|
{
|
|
/* Wait events for abort thread or rx data available. */
|
|
/* It's platform-dependent declaration. */
|
|
/* coverity[misra_c_2012_directive_4_6_violation] */
|
|
uxBits = ( PlatformEventGroup_EventBits ) PlatformEventGroup_WaitBits( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent,
|
|
( ( PlatformEventGroup_EventBits ) PKTIO_EVT_MASK_ABORT | ( PlatformEventGroup_EventBits ) PKTIO_EVT_MASK_RX_DATA ),
|
|
pdTRUE,
|
|
pdFALSE,
|
|
portMAX_DELAY );
|
|
|
|
if( ( uxBits & ( PlatformEventGroup_EventBits ) PKTIO_EVT_MASK_ABORT ) != 0U )
|
|
{
|
|
LogDebug( ( "Abort received, cleaning up!" ) );
|
|
FREE_AT_RESPONSE_AND_SET_NULL( pAtResp );
|
|
break;
|
|
}
|
|
else if( ( uxBits & ( PlatformEventGroup_EventBits ) PKTIO_EVT_MASK_RX_DATA ) != 0U )
|
|
{
|
|
/* Keep Reading until there is no more bytes in comm interface. */
|
|
do
|
|
{
|
|
bytesRead = _handleRxDataEvent( pContext, &pAtResp );
|
|
} while( ( bytesRead != 0U ) );
|
|
}
|
|
else
|
|
{
|
|
/* Empty else to avoid MISRA violation */
|
|
}
|
|
} while( true );
|
|
|
|
( void ) pContext->pCommIntf->close( pContext->hPktioCommIntf );
|
|
pContext->hPktioCommIntf = NULL;
|
|
pContext->pPktioShutdownCB( pContext );
|
|
}
|
|
else
|
|
{
|
|
LogError( ( "Comm port open failed" ) );
|
|
}
|
|
|
|
( void ) PlatformEventGroup_SetBits( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent, ( EventBits_t ) PKTIO_EVT_MASK_ABORTED );
|
|
|
|
/* Call the shutdown callback if it is defined. */
|
|
if( pContext->pPktioShutdownCB != NULL )
|
|
{
|
|
pContext->pPktioShutdownCB( pContext );
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void _PktioInitProcessReadThreadStatus( CellularContext_t * pContext )
|
|
{
|
|
PlatformEventGroup_EventBits uxBits = 0;
|
|
|
|
uxBits = ( PlatformEventGroup_EventBits ) PlatformEventGroup_WaitBits( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent,
|
|
( ( PlatformEventGroup_EventBits ) PKTIO_EVT_MASK_STARTED | ( PlatformEventGroup_EventBits ) PKTIO_EVT_MASK_ABORTED ),
|
|
pdTRUE,
|
|
pdFALSE,
|
|
( ( PlatformTickType ) ~( 0UL ) ) );
|
|
|
|
if( ( uxBits & ( PlatformEventGroup_EventBits ) PKTIO_EVT_MASK_ABORTED ) != ( PlatformEventGroup_EventBits ) PKTIO_EVT_MASK_ABORTED )
|
|
{
|
|
pContext->bPktioUp = true;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
CellularPktStatus_t _Cellular_PktioInit( CellularContext_t * pContext,
|
|
_pPktioHandlePacketCallback_t handlePacketCb )
|
|
{
|
|
CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK;
|
|
bool status = false;
|
|
|
|
if( pContext == NULL )
|
|
{
|
|
pktStatus = ( CellularPktStatus_t ) CELLULAR_PKT_STATUS_INVALID_HANDLE;
|
|
}
|
|
else
|
|
{
|
|
pContext->bPktioUp = false;
|
|
pContext->PktioAtCmdType = CELLULAR_AT_NO_COMMAND;
|
|
/* It's platform-dependent declaration. */
|
|
/* coverity[misra_c_2012_directive_4_6_violation] */
|
|
pContext->pPktioCommEvent = ( PlatformEventGroupHandle_t ) PlatformEventGroup_Create();
|
|
|
|
if( pContext->pPktioCommEvent == ( uintptr_t ) ( uintptr_t * ) NULL )
|
|
{
|
|
LogError( ( "Can't create event group" ) );
|
|
pktStatus = CELLULAR_PKT_STATUS_CREATION_FAIL;
|
|
}
|
|
}
|
|
|
|
if( pktStatus == CELLULAR_PKT_STATUS_OK )
|
|
{
|
|
pContext->pPktioHandlepktCB = handlePacketCb;
|
|
/* It's platform-dependent declaration. */
|
|
/* coverity[misra_c_2012_directive_4_6_violation] */
|
|
( void ) PlatformEventGroup_ClearBits( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent,
|
|
( ( PlatformEventGroup_EventBits ) PKTIO_EVT_MASK_ALL_EVENTS ) );
|
|
|
|
/* Create the Read thread. */
|
|
status = Platform_CreateDetachedThread( _pktioReadThread,
|
|
( void * ) pContext,
|
|
PLATFORM_THREAD_DEFAULT_PRIORITY,
|
|
PLATFORM_THREAD_DEFAULT_STACK_SIZE );
|
|
|
|
if( status == true )
|
|
{
|
|
LogDebug( ( "Reader thread created" ) );
|
|
_PktioInitProcessReadThreadStatus( pContext );
|
|
|
|
if( pContext->bPktioUp == false )
|
|
{
|
|
LogError( ( "Reader thread aborted" ) );
|
|
pktStatus = CELLULAR_PKT_STATUS_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogError( ( "Can't create reader thread" ) );
|
|
pktStatus = CELLULAR_PKT_STATUS_CREATION_FAIL;
|
|
}
|
|
}
|
|
|
|
if( pktStatus == CELLULAR_PKT_STATUS_OK )
|
|
{
|
|
LogDebug( ( "Thread create: read_thread status:%d", pktStatus ) );
|
|
}
|
|
else
|
|
{
|
|
if( pContext != NULL )
|
|
{
|
|
pContext->pPktioHandlepktCB = NULL;
|
|
|
|
if( pContext->pPktioCommEvent != ( uintptr_t ) ( uintptr_t * ) NULL )
|
|
{
|
|
/* It's platform-dependent declaration. */
|
|
/* coverity[misra_c_2012_directive_4_6_violation] */
|
|
( void ) PlatformEventGroup_Delete( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent );
|
|
pContext->pPktioCommEvent = ( PlatformEventGroupHandle_t ) ( uintptr_t ) ( uintptr_t * ) NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pktStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/* Sends the AT command to the modem. */
|
|
CellularPktStatus_t _Cellular_PktioSendAtCmd( CellularContext_t * pContext,
|
|
const char * pAtCmd,
|
|
CellularATCommandType_t atType,
|
|
const char * pAtRspPrefix )
|
|
{
|
|
uint32_t cmdLen = 0, newCmdLen = 0;
|
|
uint32_t sentLen = 0;
|
|
CellularPktStatus_t pktStatus = CELLULAR_PKT_STATUS_OK;
|
|
|
|
if( pContext == NULL )
|
|
{
|
|
LogError( ( "_Cellular_PktioSendAtCmd : invalid cellular context" ) );
|
|
pktStatus = CELLULAR_PKT_STATUS_INVALID_HANDLE;
|
|
}
|
|
else if( ( pContext->pCommIntf == NULL ) || ( pContext->hPktioCommIntf == NULL ) )
|
|
{
|
|
LogError( ( "_Cellular_PktioSendAtCmd : invalid comm interface handle" ) );
|
|
pktStatus = CELLULAR_PKT_STATUS_INVALID_HANDLE;
|
|
}
|
|
else if( pAtCmd == NULL )
|
|
{
|
|
LogError( ( "_Cellular_PktioSendAtCmd : invalid pAtCmd" ) );
|
|
pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM;
|
|
}
|
|
else
|
|
{
|
|
cmdLen = ( uint32_t ) strlen( pAtCmd );
|
|
|
|
if( cmdLen > PKTIO_WRITE_BUFFER_SIZE )
|
|
{
|
|
LogError( ( "_Cellular_PktioSendAtCmd : invalid pAtCmd" ) );
|
|
pktStatus = CELLULAR_PKT_STATUS_BAD_PARAM;
|
|
}
|
|
else
|
|
{
|
|
pContext->pRespPrefix = pAtRspPrefix;
|
|
pContext->PktioAtCmdType = atType;
|
|
newCmdLen = cmdLen;
|
|
newCmdLen += 1U; /* Include space for \r. */
|
|
|
|
( void ) strncpy( pContext->pktioSendBuf, pAtCmd, cmdLen );
|
|
pContext->pktioSendBuf[ cmdLen ] = '\r';
|
|
|
|
( void ) pContext->pCommIntf->send( pContext->hPktioCommIntf,
|
|
( const uint8_t * ) &pContext->pktioSendBuf, newCmdLen,
|
|
CELLULAR_COMM_IF_SEND_TIMEOUT_MS, &sentLen );
|
|
}
|
|
}
|
|
|
|
return pktStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/* Sends data to the modem. */
|
|
uint32_t _Cellular_PktioSendData( const CellularContext_t * pContext,
|
|
const uint8_t * pData,
|
|
uint32_t dataLen )
|
|
{
|
|
uint32_t sentLen = 0;
|
|
|
|
if( pContext == NULL )
|
|
{
|
|
LogError( ( "_Cellular_PktioSendData : invalid cellular context" ) );
|
|
}
|
|
else if( ( pContext->pCommIntf == NULL ) || ( pContext->hPktioCommIntf == NULL ) )
|
|
{
|
|
LogError( ( "_Cellular_PktioSendData : invalid comm interface handle" ) );
|
|
}
|
|
else if( pData == NULL )
|
|
{
|
|
LogError( ( "_Cellular_PktioSendData : invalid pData" ) );
|
|
}
|
|
else
|
|
{
|
|
( void ) pContext->pCommIntf->send( pContext->hPktioCommIntf, pData,
|
|
dataLen, CELLULAR_COMM_IF_SEND_TIMEOUT_MS, &sentLen );
|
|
}
|
|
|
|
LogDebug( ( "PktioSendData sent %d bytes", sentLen ) );
|
|
return sentLen;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
void _Cellular_PktioShutdown( CellularContext_t * pContext )
|
|
{
|
|
PlatformEventGroup_EventBits uxBits = 0;
|
|
|
|
if( ( pContext != NULL ) && ( pContext->bPktioUp ) )
|
|
{
|
|
if( pContext->pPktioCommEvent != ( uintptr_t ) ( uintptr_t * ) NULL )
|
|
{
|
|
( void ) PlatformEventGroup_SetBits( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent, ( EventBits_t ) PKTIO_EVT_MASK_ABORT );
|
|
/* It's platform-dependent declaration. */
|
|
/* coverity[misra_c_2012_directive_4_6_violation] */
|
|
uxBits = ( PlatformEventGroup_EventBits ) PlatformEventGroup_GetBits( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent );
|
|
|
|
while( ( PlatformEventGroup_EventBits ) ( uxBits & PKTIO_EVT_MASK_ABORTED ) != ( PlatformEventGroup_EventBits ) ( PKTIO_EVT_MASK_ABORTED ) )
|
|
{
|
|
Platform_Delay( PKTIO_SHUTDOWN_WAIT_INTERVAL_MS );
|
|
uxBits = ( PlatformEventGroup_EventBits ) PlatformEventGroup_GetBits( ( PlatformEventGroupHandle_t ) pContext->pPktioCommEvent );
|
|
}
|
|
|
|
( void ) PlatformEventGroup_Delete( pContext->pPktioCommEvent );
|
|
pContext->pPktioCommEvent = ( PlatformEventGroupHandle_t ) ( uintptr_t ) ( uintptr_t * ) NULL;
|
|
}
|
|
|
|
pContext->pPktioHandlepktCB = NULL;
|
|
pContext->bPktioUp = false;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|