Hello All,
I am back with yet another parsing question for custom PLS63 modem implementation of the Cellular Library.
I started running into some race conditions after my latest fixes on the communication interface.
While receiving data if the URC indicating socket is ready for a read comes just before a read operation were to be performed but before URC is parsed then the URC is getting treated as a response by the prefix parsing function. I am not able to wait until the data ready callback is called because mbedTLS and coreMQTT-Agent libraries poll the read function.
Here is a capture that shows the issue.
I don’t have cellular debug logs because enabling logs masks this issue because of extra timing spent on the string manipulation functions and when data is copied to the logging thread.
The format of the URC is:
“\r\n^SISR: 0,1\r\n” where 0 is the socket index and 1 indicate there are 1 or more chars to read.
The format of the read response is:
“\r\n^SISR: 0,n\r\n\r\n” where n is between 0 and requested size.
So the only difference between the two is the fact that second line has no data for the URC.
1 is a valid number that can be received while reading data so I am not able to use that to differentiate between a URC and a response. I wish the modem used any non numeric character to indicate the URC
My prefix parsing function is the following:
static CellularPktStatus_t _Cellular_SocketRecvDataPrefix( void * pCallbackContext,
char * pLine,
uint32_t lineLength,
char ** ppDataStart,
uint32_t * pDataLength )
{
(void) pCallbackContext;
*pDataLength = 0;
*ppDataStart = NULL;
if( ( pLine == NULL ) ||
( ppDataStart == NULL ) ||
( pDataLength == NULL ) ) {
LogDebug(("_Cellular_SocketRecvDataPrefix: bad parameter"));
return CELLULAR_PKT_STATUS_BAD_PARAM;
}
const size_t kMaxResponseLength = 16; // strlen("^SISR:10,15000\r\n") needs to be longer than strlen("^SISR:0,1\r\n5\r\n")
char localLine[kMaxResponseLength + 1];
// Cellular Library Parsing Functions call Validate String function
// which checks if the string is shorter than CELLULAR_AT_MAX_STRING_SIZE.
// The data response can be much longer than this because of MTU size.
// Copy the prefix part of the response and perform parsing on that.
memset(localLine, 0, kMaxResponseLength + 1);
strncpy(localLine, pLine, kMaxResponseLength);
char * pCursor = localLine;
bool bHasPrefix = false;
CellularATError_t atCoreStatus = Cellular_ATStrStartWith(pCursor,"^SISR:",&bHasPrefix);
if( atCoreStatus != CELLULAR_AT_SUCCESS ){
LogError(("_Cellular_SocketRecvDataPrefix: Cellular_ATStrStartWith error %d", atCoreStatus));
return _Cellular_TranslateAtCoreStatus( atCoreStatus );
} else if ( bHasPrefix != true) {
return CELLULAR_PKT_STATUS_OK; // Haven't received enough characters or received URC
}
// "^SISR: <socketIndex>,<dataLength>\r\n<data>..." -> "<socketIndex>,<dataLength>\r\n<data>..."
atCoreStatus = Cellular_ATRemovePrefix( &pCursor );
if( atCoreStatus != CELLULAR_AT_SUCCESS ) {
return _Cellular_TranslateAtCoreStatus( atCoreStatus );
} else if( *pCursor == '\0'){
return CELLULAR_PKT_STATUS_SIZE_MISMATCH;
}
// "<socketIndex>,<dataLength>\r\n<data>..." -> "<dataLength>\r\n<data>..."
char * pSocketIndexToken; // Unused token, skip
atCoreStatus = Cellular_ATGetNextTok( &pCursor, &pSocketIndexToken );
if( atCoreStatus != CELLULAR_AT_SUCCESS ) {
LogError(("_Cellular_SocketRecvDataPrefix: Cellular_ATGetNextTok error %d", atCoreStatus));
return _Cellular_TranslateAtCoreStatus( atCoreStatus );
} else if((pSocketIndexToken == NULL) ||
( *pCursor == '\0') ){
return CELLULAR_PKT_STATUS_SIZE_MISMATCH;
}
// "<dataLength>\r\n<data>..." -> "\r\n<data>..."
char * pSocketDataLengthToken;
const char * pDelimiter = "\r";
atCoreStatus = Cellular_ATGetSpecificNextTok( &pCursor, pDelimiter, &pSocketDataLengthToken );
if( atCoreStatus != CELLULAR_AT_SUCCESS ) {
LogError(("_Cellular_SocketRecvDataPrefix: Cellular_ATGetSpecificNextTok error %d", atCoreStatus));
return _Cellular_TranslateAtCoreStatus( atCoreStatus );
} else if((pSocketDataLengthToken == NULL) || // \r is missing, no token found
( *pCursor != '\n')){ // \n is missing, data has not started yet
return CELLULAR_PKT_STATUS_SIZE_MISMATCH;
}
// Convert data length from string to integer
int32_t socketDataLength = 0;
atCoreStatus = Cellular_ATStrtoi( pSocketDataLengthToken, 10, &socketDataLength);
if( atCoreStatus != CELLULAR_AT_SUCCESS ) {
LogError(("_Cellular_SocketRecvDataPrefix: failed to convert data length token to integer"));
return _Cellular_TranslateAtCoreStatus( atCoreStatus );
} else if( ( socketDataLength > ( int32_t ) CELLULAR_MAX_RECV_DATA_LEN) ) {
LogError(("_Cellular_SocketRecvDataPrefix: receive data length is larger than MTU"));
return CELLULAR_PKT_STATUS_BAD_RESPONSE;
} else if( socketDataLength < 0 ){ // Negative response means socket has closed
LogDebug(("_Cellular_SocketRecvDataPrefix: socket has closed"));
return CELLULAR_PKT_STATUS_SEND_ERROR;
}
pCursor++; // Skip \n, points to start of data
*pDataLength = (uint32_t) socketDataLength;
// Find the prefix length and jump forward
*ppDataStart = &pLine[pCursor-localLine];
return CELLULAR_PKT_STATUS_OK;
However, this can’t differentiate between the URC and the response so I tried to add the following snippet after the cursor increment at the end of the function:
bool possibleURC = socketDataLength == 1;
bool hasNoData = *(pCursor) != '\0';
bool dataLineFinished = (*(pCursor+1) != '\r') && (*(pCursor+2) != '\n');
if(possibleURC && ( hasNoData || !dataLineFinished)) {
return CELLULAR_PKT_STATUS_OK;
}
My goal was to detect if there is no data.
However, we can receive another URC just after the first so I also added a check for the next two characters being \r\n which would indicate there were data and this was a proper read response.
Currently this fix is not working and I am debugging it.
I also wanted to post here to get some ideas.
Thank you,
Tuna