view src/cs/services/atp/atp_cmd.c @ 303:f76436d19a7a default tip

!GPRS config: fix long-standing AT+COPS chance hanging bug There has been a long-standing bug in FreeCalypso going back years: sometimes in the AT command bring-up sequence of an ACI-only MS, the AT+COPS command would produce only a power scan followed by cessation of protocol stack activity (only L1 ADC traces), instead of the expected network search sequence. This behaviour was seen in different FC firmware versions going back to Citrine, and seemed to follow some law of chance, not reliably repeatable. This bug has been tracked down and found to be specific to !GPRS configuration, stemming from our TCS2/TCS3 hybrid and reconstruction of !GPRS support that was bitrotten in TCS3.2/LoCosto version. ACI module psa_mms.c, needed only for !GPRS, was missing in the TCS3 version and had to be pulled from TCS2 - but as it turns out, there is a new field in the MMR_REG_REQ primitive that needs to be set correctly, and that psa_mms.c module is the place where this initialization needed to be added.
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 08 Jun 2023 08:23:37 +0000
parents 4e78acac3d88
children
line wrap: on
line source

/********************************************************************************
 *
 * File Name : atp_cmd.c
 *
 * Functions handling the command translation between txt and command
 * (and vice-versa)
 *
 * (C) Texas Instruments, all rights reserved
 *
 * Version number   : 0.1 - 03-03-2000
 *
 * History          : 0.1 - Created by E. Baissus
 *                    0.2 - Reviewed : More generic AT commands handling and
 *                                     copy optimization (especially on L2CAP
 *                                     buffers)
 *                    0.3 - '+CME ERROR' and '+CMS ERROR' support
 *
 * Author           : Eric Baissus : e-baissus@ti.com
 *
 *   (C) Copyright 2000 by Texas Instruments Incorporated, All Rights Reserved
 *
 ********************************************************************************/
#include "rv/rv_general.h"
#include "rvf/rvf_api.h"
#include "atp/atp_api.h"
#include "atp/atp_i.h"
#include "atp/atp_config.h"
#include "atp/atp_cmd.h"
#include "rvm/rvm_use_id_list.h"
#include <string.h>

#ifdef BLUETOOTH
#include "l2cap_data_hdlr.h"	/* For L2CAP data handling.                     */
#endif

/********************************************************************************
* Function name	: dtoa
*
* Description   : Convert the digits of the given decimal value to a character 
*				  string
*
* Parameters    : decimal = decimal value to be converted
*                 ascii_p = string result
*
* Return        : Digit number
*
* Note          : digit and remainder declared as volatile to avoid bugs due to
*				  optimization.
*
* History       : 0.1 (25-August-2000) - Created
*
*********************************************************************************/
UINT8 dtoa (UINT16         decimal,
			T_ATP_TXT_CMD  ascii_p)
{
    /* Declare local variables.                                                 */
	T_ATP_TXT_CMD    str_p     = ascii_p;
	volatile UINT8   digit     = 0x00;
	volatile UINT16  remainder = decimal;

/***************************** dtoa function begins *****************************/

	/* Check to see if the string result is non-null.                           */
	if (str_p == NULL)
	{
		return (0x00);
	}

	/* Convert the fifth digit: remainder = [65535,9999[.                       */
	for (digit = 0x00;
		 remainder > 9999;
		 remainder -= 10000,
		 digit++)
	{
	}
	if (digit > 0x00)
	{
		*str_p++ = (INT8) ('0' + digit);
	}

	/* Convert the fourth digit: remainder = [9xxx,999[.                        */
	for (digit = 0x00;
		 remainder > 999;
		 remainder -= 1000,
		 digit++)
	{
	}
	if ((digit > 0x00) || (str_p != ascii_p))
	{
		*str_p++ = (INT8) ('0' + digit);
	}

	/* Convert the third digit: remainder = [9xx,99[.                           */
	for (digit = 0x00;
		 remainder > 99;
		 remainder -= 100,
		 digit++)
	{
	}
	if ((digit > 0x00) || (str_p != ascii_p))
	{
		*str_p++ = (INT8) ('0' + digit);
	}

	/* Convert the second digit: remainder = [9x,9[.                            */
	for (digit = 0x00;
		 remainder > 9;
		 remainder -= 10,
		 digit++)
	{
	}
	if ((digit > 0x00) || (str_p != ascii_p))
	{
		*str_p++ = (INT8) ('0' + digit);
	}

	/* Convert the last digit: remainder = [9,0].                               */
	*str_p++ = (INT8) ('0' + remainder);

	/* Return the length of the string.                                         */
	return ((UINT8) (str_p - ascii_p));

} /**************************** End of dtoa function ****************************/


/********************************************************************************
* Function name : atp_cmd_init_dce_info
*
* Description   : Initialize the whole of the information stored in the 'DCE 
*                 information' structure
*
* Parameter     : dce_info_p = pointer on the DCE information
*
* Return        : RV_OK
*
* History       : 0.1 (28-August-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_cmd_init_dce_info (T_ATP_DCE_INFO  *dce_info_p)
{
    /* Declare a local variable.                                                */
	UINT8  count = 0;

/********************* atp_cmd_init_dce_info function begins ********************/

	rvf_send_trace ("ATP : DCE information initialized ",
					34,
					NULL_PARAM,
					RV_TRACE_LEVEL_DEBUG_LOW,
					ATP_USE_ID);

	/* Define the default command line termination character (See ITU-T         */
	/* Recommendation V.250 ter page 21).                                       */
	dce_info_p->cr_character = ATP_CR_CHARACTER;

	/* Define the default response formatting character (See ITU-T              */
	/* Recommendation V.250 ter page 22).                                       */
	dce_info_p->lf_character = ATP_LF_CHARACTER;

	/* Define the default command line editing character (See ITU-T             */
	/* Recommendation V.250 ter page 22).                                       */
	dce_info_p->bs_character = ATP_BS_CHARACTER;

	/* Define the escape sequence (See ITU-T Recommendation V.250 ter page 24). */
	memcpy ((void *) (dce_info_p->escape_sequence),
			ATP_ESCAPE_SEQUENCE,
			MAX_NB_OF_CHARACTER_FOR_END_SEQUENCE);

	/* Define the command echo (See ITU-T Recommendation V.250 ter page 23).    */
	dce_info_p->echo_mode = ECHO_OFF;

	/* Define the result code suppression (See ITU-T Recommendation V.250 ter   */
	/* page 23).                                                                */
	dce_info_p->result_code_mode = RESULT_CODE_ON;

	/* Define the DCE response format (See ITU-T Recommendation V.250 ter page  */
	/* 24).                                                                     */
	dce_info_p->verbose_mode = ATP_VERBOSE_1;

	/* Initialize the masks associated with the commands executed by the ATP.   */
	memset ((void *) (dce_info_p->dce_mask),
			0x00,
			sizeof (T_ATP_DCE_MASK));

	/* Initialize pointers on temporary buffers containing the escape sequence. */
	for (count = 0;
		 count < MAX_NB_OF_CHARACTER_FOR_END_SEQUENCE;
		 count++)
	{

		/* Initialize the pointer on a temporary data buffer containing part of */
		/* the assumed escape sequence.                                         */
		dce_info_p->escape_sequence_tmp_buffer_p[count] = NULL;

		/* Initialize the number of data included in buffer hereinabove.        */
		dce_info_p->length_of_escape_sequence_tmp_buffer_p[count] = 0;
	}
	dce_info_p->nb_plus_received = 0;
	return (RV_OK);

} /******************** End of atp_cmd_init_dce_info function *******************/


/********************************************************************************
* Function name : atp_get_custom_info_from_txt
*
* Description   : Etract custom information from a text string
*
* Parameters    : text_pp = string containing the command (0-terminated)
*                 cmd_format = related structure
*                 mb_id = memory bank used to get the custom command information
*                         buffer
*                 termination_char = termination character ('\x00' or <CR>)
*
* Return        : cmd_info_pp and RV_OK if the command is found,
*                 RV_NOT_SUPPORTED
*
* Note          : Space characters are ignored and may be used freely for formatting
*                 purposes, unless they are embedded in numeric or string constants
*                 (See ITU-T Recommendation V.250 ter sections 5.4.2.1 or 5.4.2.2 on
*                 pages 6 and 7). The DCE shall be capable of accepting at least 40
*                 characters in the body.
*
* History       : 0.1 (29-August-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_get_custom_info_from_txt (T_ATP_TXT_CMD     *text_pp,
										T_ATP_CMD_FORMAT  cmd_format,
										T_RVF_MB_ID       mb_id,
										T_ATP_CMD         **cmd_info_pp,
										const char        termination_char)
{

/***************** atp_get_custom_info_from_txt function begins *****************/

	switch (cmd_format)
	{

		/* Basic syntax command does not expect any <number>.                   */
		case ATP_NO_PARAM:
			{
				*cmd_info_pp = NULL;
				return (RV_OK);
			}

		/* Basic syntax command.                                                */
		case ATP_BASIC_PARAM:
			{

				/* Allocate memory in order to store the <number> associated    */
				/* with basic syntax commands. If insufficient resources        */
				/* available, then report an internal memory error and abort.   */
				if (rvf_get_buf (mb_id, \
								 sizeof (T_ATP_BASIC_CMD), \
								 (void **) cmd_info_pp) == RVF_RED)
				{
					atp_error (ATP_ERROR_TX_MB_RED);
					return (RV_MEMORY_ERR);
				}

				/* Get the <number> of the basic syntax command (See ITU-T      */
				/* Recommendation V.250 ter page 5).                            */
				memset ((void *) (*cmd_info_pp),
						0x00,
						sizeof (T_ATP_BASIC_CMD));
				ATP_GET_NUMBER (*text_pp,
								&((*((T_ATP_BASIC_CMD **) cmd_info_pp))->number),
								'0');
				return (RV_OK);
			}

		/* Dial.                                                                */
		case ATP_DIAL_PARAM:
			{

				/* Allocate memory in order to store the <dial_string>          */
				/* associated with the dial. If insufficient resources          */
				/* available, then report an internal memory error and abort.   */
				if (rvf_get_buf (mb_id, \
								 sizeof (T_ATP_DIAL), \
								 (void **) cmd_info_pp) == RVF_RED)
				{
					atp_error (ATP_ERROR_TX_MB_RED);
					return (RV_MEMORY_ERR);
				}

				/* Get the <dial_string> of the dial (See ITU-T Recommendation  */
				/* V.250 ter page 31). All characters appearing on the same     */
				/* command line after D are considered part of the call         */
				/* addressing information to be signalled to the network, or    */
				/* modifiers used to control the signalling process, up to a    */
				/* semicolon character or the end of the command line. If the   */
				/* <dial_string> is terminated by a semicolon, the DCE does not */
				/* start the call origination procedure, but instead returns to */
				/* command state after completion of the signalling of call     */
				/* addressing information to the network.                       */
				memset ((void *) (*cmd_info_pp),
						0x00,
						sizeof (T_ATP_DIAL));
				ATP_GET_DIAL_STRING (*text_pp,
									 (*((T_ATP_DIAL **) cmd_info_pp))->dial_string_p,
									 &((*((T_ATP_DIAL **) cmd_info_pp))->dial_string_length),
									 &((*((T_ATP_DIAL **) cmd_info_pp))->call_type),
									 termination_char);
				return (RV_OK);
			}

		/* S-parameter.                                                         */
		case ATP_S_PARAM:
			{

				/* Allocate memory in order to store the <value> associated     */
				/* with S-parameters. If insufficient resources available, then */
				/* report an internal memory error and abort.                   */
				if (rvf_get_buf (mb_id, \
								 sizeof (T_ATP_S_PARAM), \
								 (void **) cmd_info_pp) == RVF_RED)
				{
					atp_error (ATP_ERROR_TX_MB_RED);
					return (RV_MEMORY_ERR);
				}

				/* Get the characters that immediately follow                   */
				/* <parameter_number>. "=" is used to set the indicated         */
				/* S-parameter to a new value (See ITU-T Recommendation V.250   */
				/* ter page 5). Note that if no value is given, the S-parameter */
				/* specified may be set to 0, or an ERROR result code issued    */
				/* and the stored value left unchanged. "?" is used to read the */
				/* current value of the indicated S-parameter (See ITU-T        */
				/* Recommendation V.250 ter page 5).                            */
				memset ((void *) (*cmd_info_pp),
						0x00,
						sizeof (T_ATP_S_PARAM));
				ATP_GET_PARAMETER_VALUE (*text_pp,
										 &((*((T_ATP_S_PARAM **) cmd_info_pp))->s_operator),
										 &((*((T_ATP_S_PARAM **) cmd_info_pp))->value));
				return (RV_OK);
			}

		/* Extended syntax command does not expect any <value>.                 */
		case ATP_NO_EXTENDED_PARAM:
			{

				/* Allocate memory in order to store the <value> associated     */
				/* with extended syntax commands. If insufficient resources     */
				/* available, then report an internal memory error and abort.   */
				if (rvf_get_buf (mb_id, \
								 sizeof (T_ATP_NO_SUBPARAMETER), \
								 (void **) cmd_info_pp) == RVF_RED)
				{
					atp_error (ATP_ERROR_TX_MB_RED);
					return (RV_MEMORY_ERR);
				}

				/* Get the characters that immediately follow <name>. "=?" is   */
				/* used to test whether the extended syntax command is          */
				/* implemented in the DCE (See ITU-T Recommendation V.250 ter   */
				/* page 9).                                                     */
				memset ((void *) (*cmd_info_pp),
						0x00,
						sizeof (T_ATP_NO_SUBPARAMETER));
				ATP_GET_OPERATOR (*text_pp,
								  &((*((T_ATP_NO_SUBPARAMETER **) cmd_info_pp))->extended_operator));
				return (RV_OK);
			}

		/* Extended syntax command whose subparameter is a numeric constant.    */
		case ATP_SINGLE_EXTENDED_PARAM:
			{

				/* Allocate memory in order to store the <value> associated     */
				/* with extended syntax commands. If insufficient resources     */
				/* available, then report an internal memory error and abort.   */
				if (rvf_get_buf (mb_id, \
								 sizeof (T_ATP_SINGLE_SUBPARAMETER), \
								 (void **) cmd_info_pp) == RVF_RED)
				{
					atp_error (ATP_ERROR_TX_MB_RED);
					return (RV_MEMORY_ERR);
				}

				/* Get the characters that immediately follow <name>. "=" is    */
				/* used to set the indicated extended syntax command to a new   */
				/* value (See ITU-T Recommendation V.250 ter page 8). Note that */
				/* if no value is given, the extended syntax command specified  */
				/* may be set to 0. "?" is used to read the current value of    */
				/* the indicated extended syntax command (See ITU-T             */
				/* Recommendation V.250 ter page 9). "=?" is used to test       */
				/* whether the extended syntax command is implemented in the    */
				/* DCE.                                                         */
				memset ((void *) (*cmd_info_pp),
						0x00,
						sizeof (T_ATP_SINGLE_SUBPARAMETER));
				ATP_GET_VALUE (*text_pp,
							   &((*((T_ATP_SINGLE_SUBPARAMETER **) cmd_info_pp))->extended_operator),
							   &((*((T_ATP_SINGLE_SUBPARAMETER **) cmd_info_pp))->value),
							   termination_char);
				return (RV_OK);
			}

		/* Keypad control command.                                              */
		case ATP_PLUS_CKPD_PARAM:
			{

				/* Allocate memory in order to store the <keys>,<time> and      */
				/* <pause> associated with +CKPD extended syntax command. If    */
				/* insufficient resources available, then report an internal    */
				/* memory error and abort.                                      */
				if (rvf_get_buf (mb_id, \
								 sizeof (T_ATP_PLUS_CKPD), \
								 (void **) cmd_info_pp) == RVF_RED)
				{
					atp_error (ATP_ERROR_TX_MB_RED);
					return (RV_MEMORY_ERR);
				}

				/* Get the <keys>,<time> and <pause> of the keypad control      */
				/* command. Note that <keys> shall consist of a string constant */
				/* and <time> and <pause> shall consist of numeric constants in */
				/* tenths of a second (See ETS 300 916 (GSM 07.07) Version      */
				/* 5.8.1 page 62). "=?" is used to test whether the extended    */
				/* syntax command is implemented in the DCE.                    */
				memset ((void *) (*cmd_info_pp),
						0x00,
						sizeof (T_ATP_PLUS_CKPD));
				ATP_GET_CKPD_PARAM (*text_pp,
									(*((T_ATP_PLUS_CKPD **) cmd_info_pp)),
									termination_char);
				return (RV_OK);
			}

		/* AT command undefined or not supported for now.                       */
		default:
			{
				break;
			}
	}
	return (RV_NOT_SUPPORTED);

} /**************** End of atp_get_custom_info_from_txt function ****************/


/********************************************************************************
* Function name : atp_translate_raw_data_to_cmd
*
* Description   : Translate raw data into an interpreted command
*
* Parameters    : dce_info_p = pointer on the port structure
*                 text_p = raw data containing the command (0-terminated)
*                 cmd_type = command type of the text
*                 cmd_type_p = type of the interpreted command
*                 cmd_nb_p = binary related code of the interpreted command
*                 skip_prefix = indicate whether the prefix shall be skipped in
*                               order to proceed the translation
*                 mb_id = memory bank used to get the custom command information
*                         buffer
*                 cmd_info_pp = pointer on the custom command information structure
*
* Return        : RV_OK,
*                 RV_NOT_SUPPORTED if the command is not recognized,
*                 RV_INVALID_PARAMETER
*
* Note          : A command line is made up of three elements: the prefix, the body,
*                 and the termination character. The command line prefix consists of
*                 the characters "AT" or "at", or, to repeat the execution of the 
*                 previous command line, the characters "A/" or "a/". The body is
*                 made up of individual commands. Space characters are ignored and
*                 may be used freely for formatting purposes, unless they are embedded
*                 in numeric or string constants (See ITU-T Recommendation V.250 ter
*                 sections 5.4.2.1 or 5.4.2.2 on pages 6 and 7). The termination
*                 character may not appear in the body. The DCE shall be capable of
*                 accepting at least 40 characters in the body. Note that the
*                 termination character may be selected by a user option (parameter
*                 S3), the default being CR.
*
* History       : 0.1 (25-August-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_translate_raw_data_to_cmd (T_ATP_PORT_STRUCT  *port_p,
										 T_ATP_TXT_CMD      *text_pp,
										 T_ATP_CMD_TYPE     cmd_type,
										 T_ATP_CMD_TYPE     *cmd_type_p,
										 T_ATP_CMD_NB       *cmd_nb_p,
										 T_RVF_MB_ID        mb_id,
										 T_ATP_CMD          **cmd_info_pp)
{
	/* Declare a local variable.                                                */
	BOOLEAN  equal = FALSE;

/***************** atp_translate_raw_data_to_cmd function begins ****************/

	/* Check to see if the text command line is valid.                          */
	if ((text_pp == NULL) || (*text_pp == NULL))
	{
		return (RV_INVALID_PARAMETER);
	}

	/* Initialize information for AT commands do not expect any number or       */
	/* value.                                                                   */
	*cmd_info_pp = NULL;

	/* DTE command lines (See ITU-T Recommendation V.250 ter page 4). By the    */
	/* way, note that the prefix consists of the characters "AT" or "at".       */
	if ((cmd_type == UNKNOWN) || (cmd_type == AT_CMD))
	{

		/* Declare a local block variable.                                      */
		UINT8  prefix_len = 0x00;

		/* Check to see whether the prefix shall be skipped in order to proceed */
		/* the translation.                                                     */
		if ((port_p == NULL) || ((port_p->cmd_info).status == NOT_STARTED))
		{
			prefix_len = ATP_AT_PREFIX_LEN;
		}

		/* If the prefix is either "AT" or "at", then the DCE shall proceed the */
		/* command line body (See ITU-T Recommendation V.250 ter page 4).       */
		ATP_MEM_I_CMP (ATP_AT_PREFIX,
					   *text_pp,
					   prefix_len,
					   &equal);
		if (equal == TRUE)
		{

			/* Declare local block variables.                                   */
			const char  *cmd_p  = NULL;
			UINT8       cmd_len = 0x00;
			
			for (*cmd_nb_p   = 0x00, \
				 *cmd_type_p = AT_CMD, \
				 equal       = FALSE, \
				 *text_pp    += prefix_len;
				 *cmd_nb_p < ATP_MAX_NB_OF_AT_COMMANDS;
				 (*cmd_nb_p)++)
			{

				/* If needed, skip this empty entry.                            */
				if (ATP_AT_INFO[*cmd_nb_p][ATP_AT_PARAM_COLUMN] == ATP_CMD_NOT_DEFINED)
				{
					continue;
				}

				/* Get the length of the remainder.                             */
				cmd_len = (UINT8) (ATP_AT_INFO[*cmd_nb_p + 0x01][ATP_AT_OFFSET_COLUMN] - ATP_AT_INFO[*cmd_nb_p][ATP_AT_OFFSET_COLUMN]);
				cmd_p   = &(ATP_AT_TXT_TABLE[ATP_AT_INFO[*cmd_nb_p][ATP_AT_OFFSET_COLUMN]]);
				ATP_MEM_SP_I_CMP (cmd_p,
								  *text_pp,
								  &cmd_len,
								  &equal);

				/* If both AT commands match, then get the custom information.  */
				if (equal == TRUE)
				{
					rvf_send_trace ("ATP : Translate an AT command from text to command ",
									51,
									NULL_PARAM,
									RV_TRACE_LEVEL_DEBUG_LOW,
									ATP_USE_ID);
					*text_pp += cmd_len;
					(void) atp_get_custom_info_from_txt (text_pp,
														 ATP_AT_INFO[*cmd_nb_p][ATP_AT_PARAM_COLUMN],
														 mb_id,
														 cmd_info_pp,
														 (const char) ((port_p == NULL) ? ('\x00') : ((port_p->dce_info_p)->cr_character)));
					return (RV_OK);
				}
			}
			return (RV_NOT_SUPPORTED);
		}
	}

	/* DCE responses (See ITU-T Recommendation V.250 ter page 10).              */
	if ((cmd_type == UNKNOWN) || (cmd_type == RESULT_CODE) || \
		(cmd_type == UNSOLICITED_RESULT))
	{

		/* Declare local block variables.                                       */
		const char  *result_code_p  = NULL;
		UINT8       header_len      = 0x00;
		UINT8       result_code_len = 0x00;

		/* If verbose responses are enabled, check to see whether leading <CR>  */
		/* and <LF> characters shall be skipped in order to proceed the         */
		/* translation.                                                         */
		if ((port_p != NULL) && ((port_p->dce_info_p)->verbose_mode == ATP_VERBOSE_1))
		{
			equal = TRUE;
			equal &= ((*text_pp)[0x00] == (port_p->dce_info_p)->cr_character);
			equal &= ((*text_pp)[0x01] == (port_p->dce_info_p)->lf_character);

			/* If leading characters do not match <CR><LF> headers of           */
			/* information responses, then abort (See ETS 300 916 (GSM 07.07)   */
			/* Version 4.1 page 13).                                            */
			if (equal == FALSE)
			{
				*cmd_type_p = UNKNOWN;
				return (RV_NOT_SUPPORTED);
			}
			header_len = ATP_RESULT_CODE_HEADER_LEN;
		}
		for (*cmd_nb_p   = 0x00, \
			 *cmd_type_p = RESULT_CODE, \
			 *text_pp    += header_len;
			 *cmd_nb_p < ATP_MAX_NB_OF_RESULT_CODES;
			 (*cmd_nb_p)++)
		{

			/* If needed, skip this empty entry.                                */
			if (ATP_RESULT_CODE_INFO[*cmd_nb_p][ATP_RESULT_PARAM_COLUMN] == ATP_RESULT_CODE_NOT_DEFINED)
			{
				continue;
			}

			/* If verbose responses are disabled, then get the length of the    */
			/* result code from the dedicated list.                             */
			if ((port_p != NULL) && ((port_p->dce_info_p)->verbose_mode == ATP_VERBOSE_0))
			{
				result_code_len = (UINT8) (ATP_RESULT_CODE_INFO[*cmd_nb_p + 0x01][ATP_RESULT_OFFSET_V0_COLUMN] - ATP_RESULT_CODE_INFO[*cmd_nb_p][ATP_RESULT_OFFSET_V0_COLUMN]);
				result_code_p   = &(ATP_RESULT_CODE_TXT_TABLE_V0[ATP_RESULT_CODE_INFO[*cmd_nb_p][ATP_RESULT_OFFSET_V0_COLUMN]]);
			}
			else
			{
				result_code_len = (UINT8) (ATP_RESULT_CODE_INFO[*cmd_nb_p + 0x01][ATP_RESULT_OFFSET_V1_COLUMN] - ATP_RESULT_CODE_INFO[*cmd_nb_p][ATP_RESULT_OFFSET_V1_COLUMN]);
				result_code_p   = &(ATP_RESULT_CODE_TXT_TABLE_V1[ATP_RESULT_CODE_INFO[*cmd_nb_p][ATP_RESULT_OFFSET_V1_COLUMN]]);
			}
			ATP_MEM_I_CMP (result_code_p,
						   *text_pp,
						   result_code_len,
						   &equal);
			if (equal == FALSE)
			{
				continue;
			}
			rvf_send_trace ("ATP : Translate a result from text to command ",
							46,
							NULL_PARAM,
							RV_TRACE_LEVEL_DEBUG_LOW,
							ATP_USE_ID);
			*text_pp += result_code_len;
			switch (ATP_RESULT_CODE_INFO[*cmd_nb_p][ATP_RESULT_PARAM_COLUMN])
			{

				/* Extended syntax result code.                                 */
				case ATP_EXTENDED_RESULT_CODE:
					{

						/* Allocate memory in order to store the <value>        */
						/* associated with extended syntax result codes. If     */
						/* insufficient resources available, then report an     */
						/* internal memory error and abort.                     */
						if (rvf_get_buf (mb_id, \
										 sizeof (T_ATP_SINGLE_RESULT_CODE_VALUE), \
										 (void **) cmd_info_pp) == RVF_RED)
						{
							atp_error (ATP_ERROR_TX_MB_RED);
							return (RV_MEMORY_ERR);
						}

						/* Get the value associated with the extended result    */
						/* codes (See Headset Specification, Section 4.7.3).    */
						memset ((void *) (*cmd_info_pp),
								0x00,
								sizeof (T_ATP_SINGLE_RESULT_CODE_VALUE));
						ATP_GET_RESULT_CODE_VALUE (*text_pp,
												   &((*((T_ATP_SINGLE_RESULT_CODE_VALUE **) cmd_info_pp))->value));
						break;
					}

				/* +CME ERROR: <error> and +CMS ERROR: <error> result codes.    */
				case ATP_PLUS_ERROR_RESULT_CODE:
					{

						/* Allocate memory in order to store the <error>        */
						/* associated with +CME ERROR or +CMS ERROR result      */
						/* codes. If insufficient resources available, then     */
						/* report an internal memory error and abort.           */
						if (rvf_get_buf (mb_id, \
										 sizeof (T_ATP_PLUS_ERROR_RESULT_CODE), \
										 (void **) cmd_info_pp) == RVF_RED)
						{
							atp_error (ATP_ERROR_TX_MB_RED);
							return (RV_MEMORY_ERR);
						}

						/* Get the <error> associated with the +CME ERROR or    */
						/* +CMS ERROR result codes.                             */
						memset ((void *) (*cmd_info_pp),
								0x00,
								sizeof (T_ATP_PLUS_ERROR_RESULT_CODE));
						ATP_PLUS_ERROR_STRING (*text_pp,
											   ((T_ATP_PLUS_ERROR_RESULT_CODE *) (*cmd_info_pp))->error_p,
											   &(((T_ATP_PLUS_ERROR_RESULT_CODE *) (*cmd_info_pp))->error_length),
											   '\x00');
						break;
					}
				default:
					{

						/* Check to see if any text is associated with the      */
						/* CONNECT result code.                                 */
						if (*cmd_nb_p == ATP_CONNECT_NB)
						{

							/* Declare a local block variable.                  */
							UINT16  connect_text = 0x0000;

							/* Get the <text> associated with the CONNECT       */
							/* result codes (See ITU-T Recommendation V.250 ter */
							/* page 11).                                        */
							ATP_GET_CONNECT_TXT (*text_pp,
												 &connect_text);

							/* If no <text> is associated with the CONNECT      */
							/* result code, then abort.                         */
							if (connect_text == 0x0000)
							{
								break;
							}

							/* Otherwise, allocate memory in order to store the */
							/* <text> associated with the CONNECT result code.  */
							/* If insufficient resources available, then report */
							/* an internal memory error and abort.              */
							if (rvf_get_buf (mb_id, \
											 sizeof (T_ATP_CONNECT_TXT_PARAM), \
											 (void **) cmd_info_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								return (RV_MEMORY_ERR);
							}

							/* Return the <text> associated with the CONNECT    */
							/* result code.                                     */
							(*((T_ATP_CONNECT_TXT_PARAM **) cmd_info_pp))->value = connect_text;
							*cmd_nb_p = ATP_CONNECT_TXT_NB;
						}
						break;
					}
			}
			return (RV_OK);
		}
	}
	*cmd_type_p = UNKNOWN;
	return (RV_NOT_SUPPORTED);

} /**************** End of atp_translate_raw_data_to_cmd function ***************/


/********************************************************************************
* Function name : atp_translate_txt_to_cmd
*
* Description   : Translate a text string into an interpreted command
*
* Parameters    : text_p = text string containing the command (0-terminated)
*                 cmd_type = command type of the text
*                 cmd_type_p = type of the interpreted command
*                 cmd_nb_p = binary related code of the interpreted command
*                 mb_id = memory bank used to get the custom command information
*                         buffer
*                 cmd_info_pp = pointer on the custom command information structure
*
* Return        : RV_OK,
*                 RV_NOT_SUPPORTED if the command is not recognized,
*                 RV_INVALID_PARAMETER
*
* History       : 0.1 (25-August-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_translate_txt_to_cmd (T_ATP_TXT_CMD   text_p,
									T_ATP_CMD_TYPE  cmd_type,
									T_ATP_CMD_TYPE  *cmd_type_p,
									T_ATP_CMD_NB    *cmd_nb_p,
									T_RVF_MB_ID     mb_id,
									T_ATP_CMD       **cmd_info_pp)
{
	/* Declare a local variable.                                                */
	T_ATP_RET  ret_status = RV_OK;
		
/******************* atp_translate_txt_to_cmd function begins *******************/

	ret_status = atp_translate_raw_data_to_cmd (NULL,
												&text_p,
												cmd_type,
												cmd_type_p,
												cmd_nb_p,
												mb_id,
												cmd_info_pp);
	return (ret_status);

} /****************** End of atp_translate_txt_to_cmd function ******************/


/********************************************************************************
* Function name : atp_interpret_raw_data
*
* Description   : Fetch and interpret (if applicable and DCE mode) a new command
*                 from the raw data buffer stored available in port_structure
*
* Parameters    : port_p = pointer on the port structure
*                 mb_id = memory bank which the text buffer should be counted on
*                 cmd_type_p = found command type (if UNKNOWN, should check text
*                              field)
*                 cmd_nb_p = found command number
*                 cmd_info_p = pointer on the custom command information structure
*                 text_pp = result of interpretation: contain next command to be
*                           sent by ATP in text format (0-terminated) or NULL if
*                           no command to be sent
*                 text_length_p = length of the text command, '\x00' not included
*
* Return        : RV_MEMORY_ERR in case 'memory bank' get RED,
*                 RV_NOT_SUPPORTED if the buffer does not contain proper data,
*                 RV_OK otherwise
*
* Note          : The first data that should be checked are:
*                 (port_p->cmd_info).cmd_txt_p[(port_p->cmd_info).next_position]
*                 if (port_p->cmd_info).status = NOT_STARTED, an 'AT' should be
*                 found
*                 if the function has processed all the chain, it should set state
*                 to FINISHED
*                 (port_p->cmd_info).next_position must be updated by the function
*
* History       : 0.1 (25-August-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_interpret_raw_data (T_ATP_PORT_STRUCT	 *port_p,
								  T_RVF_MB_ID        mb_id,
								  T_ATP_CMD_TYPE     *cmd_type_p,
								  T_ATP_CMD_NB       *cmd_nb_p,
								  T_ATP_CMD          **cmd_info_pp,
								  T_ATP_TXT_CMD      *text_pp,
								  UINT16             *text_length_p)
{
	/* Declare local variables.                                                 */
	T_ATP_RET      ret_status = RV_OK;
	T_ATP_TXT_CMD  text_p     = NULL;

/******************** atp_interpret_raw_data function begins ********************/

	rvf_send_trace ("ATP : Interpret raw data ",
					25,
					NULL_PARAM,
					RV_TRACE_LEVEL_DEBUG_LOW,
					ATP_USE_ID);

	*text_pp = NULL;

	/* Interpret all AT commands or result codes.                               */
	while (((port_p->cmd_info).status == NOT_STARTED) || \
		   (((port_p->cmd_info).status == ON_GOING) && \
		   ((port_p->cmd_info).cmd_txt_p[(port_p->cmd_info).next_position] != (port_p->dce_info_p)->cr_character) && \
		   ((port_p->cmd_info).next_position < (port_p->cmd_info).cmd_txt_length)))
	{
		text_p     = &((port_p->cmd_info).cmd_txt_p[(port_p->cmd_info).next_position]);
		ret_status = atp_translate_raw_data_to_cmd (port_p,
													&text_p,
													UNKNOWN,
													cmd_type_p,
													cmd_nb_p,
													mb_id,
													cmd_info_pp);
		
		/* If any memory error occurred, then abort.                            */
		if (ret_status == RV_MEMORY_ERR)
		{
			return (RV_MEMORY_ERR);
		}
		
		/* Backup the next character to be interpreted.                         */
		(port_p->cmd_info).status        = ON_GOING;
		(port_p->cmd_info).next_position = (UINT16) (text_p - (port_p->cmd_info).cmd_txt_p);
		
		/* Intrepret the extracted command.                                     */
		switch (*cmd_type_p)
		{
			
			/* Interpret the extracted AT command.                              */
			case AT_CMD:
				{

					/* Interpret AT commands only if ATP is acting as a DCE.	*/
					if (port_p->port_config != DCE_CONFIG)
					{
						break;
					}

					/* Set the result of interpretation to NULL.                */
					*text_pp = NULL;
					switch (*cmd_nb_p)
					{
						
						/* Answer. Note that any additional commands that       */
						/* appear after A on the same command line are ignored  */
						/* (See ITU-T Recommendation V.250 ter page 35).        */
						case ATP_ATA_NB:
						
						/* Dial. Note that all characters appearing on the same */
						/* command line after D are considered part of the call */
						/* addressing information to be signalled to the        */
						/* network, or modifiers used to control the signalling */
						/* process, up to a semicolon character or the end of   */
						/* the command line (See ITU-T Recommendation V.250 ter */
						/* page 31). Note also that the ITU-T Recommendation    */
						/* V.250 ter does not describe DCE behaviour in some    */
						/* situations. Thus, additional characters that appear  */
						/* on the same command line after a semicolon that      */
						/* terminates dial string are either ignored or         */
						/* processed as commands (See ITU-T Recommendation      */
						/* V.250 ter page 14). In our implementation, such      */
						/* additional characters are ignored.                   */
						case ATP_ATD_NB:
						
						/* Reset to default configuration. Note that any        */
						/* additional commands that appear on the same command  */
						/* line after Z are ignored (See ITU-T Recommendation   */
						/* V.250 ter page  15).                                 */
						case ATP_ATZ_NB:
							{
								(port_p->cmd_info).next_position = (port_p->cmd_info).cmd_txt_length;
								break;
							}
						
						/* Command echo.                                        */
						case ATP_ATE_NB:
							{
								
								/* Check to see if the specified value is valid */
								/* (See ITU-T Recommendation V.250 ter page     */
								/* 23).                                         */
								if (((*((T_ATP_ATE_PARAM **) cmd_info_pp))->number != ECHO_OFF) && \
									((*((T_ATP_ATE_PARAM **) cmd_info_pp))->number != ECHO_ON))
								{
									rvf_send_trace ("ATP : Command echo value invalid ",
													33,
													NULL_PARAM,
													RV_TRACE_LEVEL_WARNING,
													ATP_USE_ID);
									rvf_free_buf (*cmd_info_pp);
									*cmd_info_pp = NULL;
									return (RV_NOT_SUPPORTED);
								}
								rvf_send_trace ("ATP : Command echo updated ",
												27,
												NULL_PARAM,
												RV_TRACE_LEVEL_DEBUG_LOW,
												ATP_USE_ID);
								(port_p->dce_info_p)->echo_mode = (*((T_ATP_ATE_PARAM **) cmd_info_pp))->number;
								*cmd_type_p                     = UNKNOWN;
								rvf_free_buf (*cmd_info_pp);
								*cmd_info_pp = NULL;
								
								/* Interpret the next AT command or result code */
								/* to come.                                     */
								continue;
							}
							
						/* Result code suppression.                             */
						case ATP_ATQ_NB:
							{
								
								/* Check to see if the specified value is valid */
								/* (See ITU-T Recommendation V.250 ter page     */
								/* 23).                                         */
								if (((*((T_ATP_ATQ_PARAM **) cmd_info_pp))->number != RESULT_CODE_ON) && \
									((*((T_ATP_ATQ_PARAM **) cmd_info_pp))->number != RESULT_CODE_OFF))
								{
									rvf_send_trace ("ATP : Result code suppression value invalid ",
													44,
													NULL_PARAM,
													RV_TRACE_LEVEL_WARNING,
													ATP_USE_ID);
									rvf_free_buf (*cmd_info_pp);
									*cmd_info_pp = NULL;
									return (RV_NOT_SUPPORTED);
								}
								rvf_send_trace ("ATP : Result code suppression updated ",
												38,
												NULL_PARAM,
												RV_TRACE_LEVEL_DEBUG_LOW,
												ATP_USE_ID);
								(port_p->dce_info_p)->result_code_mode = (*((T_ATP_ATQ_PARAM **) cmd_info_pp))->number;
								*cmd_type_p                            = UNKNOWN;
								rvf_free_buf (*cmd_info_pp);
								*cmd_info_pp = NULL;
								
								/* Interpret the next AT command or result code */
								/* to come.                                     */
								continue;
							}
							
						/* DCE response format.                                 */
						case ATP_ATV_NB:
							{
								
								/* Check to see if the specified value is valid */
								/* (See ITU-T Recommendation V.250 ter page     */
								/* 24).                                         */
								if (((*((T_ATP_ATV_PARAM **) cmd_info_pp))->number != ATP_VERBOSE_0) && \
									((*((T_ATP_ATV_PARAM **) cmd_info_pp))->number != ATP_VERBOSE_1))
								{
									rvf_send_trace ("ATP : DCE response format value invalid ",
													40,
													NULL_PARAM,
													RV_TRACE_LEVEL_WARNING,
													ATP_USE_ID);
									rvf_free_buf (*cmd_info_pp);
									*cmd_info_pp = NULL;
									return (RV_NOT_SUPPORTED);
								}
								rvf_send_trace ("ATP : DCE response format updated ",
												34,
												NULL_PARAM,
												RV_TRACE_LEVEL_DEBUG_LOW,
												ATP_USE_ID);
								(port_p->dce_info_p)->verbose_mode = (*((T_ATP_ATV_PARAM **) cmd_info_pp))->number;
								*cmd_type_p                        = UNKNOWN;
								rvf_free_buf (*cmd_info_pp);
								*cmd_info_pp = NULL;
								
								/* Interpret the next AT command or result code */
								/* to come.                                     */
								continue;
							}
							
						/* Command line termination character.                  */
						case ATP_ATS3_NB:
							{
								
								/* Check to see if the specified value is valid */
								/* (See ITU-T Recommendation V.250 ter pages 21 */
								/* and 22).                                     */
								if ((((*((T_ATP_ATS3_PARAM **) cmd_info_pp))->s_operator != READ_S_PARAM) && \
									((*((T_ATP_ATS3_PARAM **) cmd_info_pp))->s_operator != SET_S_PARAM)) || \
									((*((T_ATP_ATS3_PARAM **) cmd_info_pp))->value > 0x007F))
								{
									rvf_send_trace ("ATP : Command line termination character invalid ",
													49,
													NULL_PARAM,
													RV_TRACE_LEVEL_WARNING,
													ATP_USE_ID);
									rvf_free_buf (*cmd_info_pp);
									*cmd_info_pp = NULL;
									return (RV_NOT_SUPPORTED);
								}
								rvf_send_trace ("ATP : Command line termination character updated ",
												49,
												NULL_PARAM,
												RV_TRACE_LEVEL_DEBUG_LOW,
												ATP_USE_ID);
								
								/* Check to see if the S-parameter is requested */
								/* to be set to a new value.                    */
								if ((*((T_ATP_ATS3_PARAM **) cmd_info_pp))->s_operator == SET_S_PARAM)
								{
									(port_p->dce_info_p)->cr_character = (char) ((*((T_ATP_ATS3_PARAM **) cmd_info_pp))->value);
									*cmd_type_p                        = UNKNOWN;
									rvf_free_buf (*cmd_info_pp);
									*cmd_info_pp = NULL;
									
									/* Interpret the next AT command or result  */
									/* code to come.                            */
									continue;
								}
								break;
							}
							
						/* Response formatting character.                       */
						case ATP_ATS4_NB:
							{
								
								/* Check to see if the specified value is valid */
								/* (See ITU-T Recommendation V.250 ter page     */
								/* 22).                                         */
								if ((((*((T_ATP_ATS4_PARAM **) cmd_info_pp))->s_operator != READ_S_PARAM) && \
									((*((T_ATP_ATS4_PARAM **) cmd_info_pp))->s_operator != SET_S_PARAM)) || \
									((*((T_ATP_ATS4_PARAM **) cmd_info_pp))->value > 0x007F))
								{
									rvf_send_trace ("ATP : Response formatting character invalid ",
													44,
													NULL_PARAM,
													RV_TRACE_LEVEL_WARNING,
													ATP_USE_ID);
									rvf_free_buf (*cmd_info_pp);
									*cmd_info_pp = NULL;
									return (RV_NOT_SUPPORTED);
								}
								rvf_send_trace ("ATP : Response formatting character updated ",
												44,
												NULL_PARAM,
												RV_TRACE_LEVEL_DEBUG_LOW,
												ATP_USE_ID);
								
								/* Check to see if the S-parameter is requested */
								/* to be set to a new value.                    */
								if ((*((T_ATP_ATS4_PARAM **) cmd_info_pp))->s_operator == SET_S_PARAM)
								{
									(port_p->dce_info_p)->lf_character = (char) ((*((T_ATP_ATS4_PARAM **) cmd_info_pp))->value);
									*cmd_type_p                        = UNKNOWN;
									rvf_free_buf (*cmd_info_pp);
									*cmd_info_pp = NULL;
									
									/* Interpret the next AT command or result  */
									/* code to come.                            */
									continue;
								}
								break;
							}
							
						/* Command line editing character.                      */
						case ATP_ATS5_NB:
							{
								
								/* Check to see if the specified value is valid */
								/* (See ITU-T Recommendation V.250 ter page     */
								/* 22).                                         */
								if ((((*((T_ATP_ATS5_PARAM **) cmd_info_pp))->s_operator != READ_S_PARAM) && \
									((*((T_ATP_ATS5_PARAM **) cmd_info_pp))->s_operator != SET_S_PARAM)) || \
									((*((T_ATP_ATS5_PARAM **) cmd_info_pp))->value > 0x007F))
								{
									rvf_send_trace ("ATP : Command line editing character invalid ",
													45,
													NULL_PARAM,
													RV_TRACE_LEVEL_WARNING,
													ATP_USE_ID);
									rvf_free_buf (*cmd_info_pp);
									*cmd_info_pp = NULL;
									return (RV_NOT_SUPPORTED);
								}
								rvf_send_trace ("ATP : Command line editing character updated ",
												45,
												NULL_PARAM,
												RV_TRACE_LEVEL_DEBUG_LOW,
												ATP_USE_ID);
								
								/* Check to see if the S-parameter is requested */
								/* to be set to a new value.                    */
								if ((*((T_ATP_ATS5_PARAM **) cmd_info_pp))->s_operator == SET_S_PARAM)
								{
									(port_p->dce_info_p)->bs_character = (char) ((*((T_ATP_ATS5_PARAM **) cmd_info_pp))->value);
									*cmd_type_p                        = UNKNOWN;
									rvf_free_buf (*cmd_info_pp);
									*cmd_info_pp = NULL;
									
									/* Interpret the next AT command or result  */
									/* code to come.                            */
									continue;
								}
								break;
							}

						/* AT command not recognized.                           */
						case ATP_MAX_NB_OF_AT_COMMANDS:
							{

								/* Get the length of the AT command to be       */
								/* returned, '\x00' not included.               */
								ATP_GET_UNKNOWN_AT_CMD_LEN (text_p,
															text_length_p,
															(port_p->dce_info_p)->cr_character);

								/* Allocate memory in order to return the AT    */
								/* command ('\x00' included). Note that the     */
								/* prefix must be taken into account. If        */
								/* insufficient resources available, then       */
								/* report an internal memory error and abort.   */
								if (rvf_get_buf (mb_id, \
												 ATP_AT_PREFIX_LEN + *text_length_p + 0x0001, \
												 (void **) text_pp) == RVF_RED)
								{
									atp_error (ATP_ERROR_TX_MB_RED);
									ret_status = RV_MEMORY_ERR;
									break;
								}

								/* Copy the prefix into the buffer (See ITU-T   */
								/* Recommendation V.250 ter page 4).            */
								memcpy ((void *) *text_pp,
										ATP_AT_PREFIX,
										ATP_AT_PREFIX_LEN);

								/* Then, copy the command line body into the    */
								/* buffer.                                      */
								memcpy ((void *) &((*text_pp)[ATP_AT_PREFIX_LEN]),
										(void *) text_p,
										*text_length_p);
								(*text_pp)[*text_length_p + ATP_AT_PREFIX_LEN] = '\x00';
								text_p                                         += *text_length_p;
								break;
							}

						/* Other recognized AT commands.                        */
						default:
							{
								break;
							}
					}

					/* If the extracted AT command is an extended syntax        */
					/* command, then update the position of the next character  */
					/* to be interpreted.                                       */
					switch (ATP_AT_INFO[*cmd_nb_p][ATP_AT_PARAM_COLUMN])
					{

						/* AT command undefined or not supported for now.       */
						case ATP_CMD_NOT_DEFINED:

						/* Extended syntax command does not expect any <value>. */
						case ATP_NO_EXTENDED_PARAM:

						/* Extended syntax command whose subparameter is a      */
						/* numeric constant.                                    */
						case ATP_SINGLE_EXTENDED_PARAM:

						/* Keypad control command.                              */
						case ATP_PLUS_CKPD_PARAM:
							{
								text_p                           += ((*text_p == (port_p->dce_info_p)->cr_character) ? (0x0000) : (0x0001));
								(port_p->cmd_info).next_position = (UINT16) (text_p - (port_p->cmd_info).cmd_txt_p);
							}
						default:
							{
								break;
							}
					}
					break;
				}

			/* Return raw data as received.									    */
			case UNKNOWN:
				{

					/* Get the length of raw data to be returned, '\x00' not    */
					/* included.                                                */
					ATP_GET_UNKNOWN_CMD_LEN (text_p,
											 text_length_p,
											 (port_p->dce_info_p)->cr_character);

					/* Allocate memory in order to return raw data ('\x00'      */
					/* included). If insufficient resources available, then     */
					/* report an internal memory error and abort.               */
					if (rvf_get_buf (mb_id, \
									 *text_length_p + 0x0001, \
									 (void **) text_pp) == RVF_RED)
					{
						atp_error (ATP_ERROR_TX_MB_RED);
						ret_status = RV_MEMORY_ERR;
						break;
					}

					/* Copy raw data into the buffer.                           */
					memcpy ((void *) *text_pp,
							(void *) text_p,
							*text_length_p);
					(*text_pp)[*text_length_p] = '\x00';
					text_p                     += *text_length_p;

					/* Backup the next character to be interpreted.             */
					(port_p->cmd_info).next_position = (UINT16) (text_p - (port_p->cmd_info).cmd_txt_p);
					break;
				}

			/* Else, result codes not supported.                                */
			case RESULT_CODE:
			case UNSOLICITED_RESULT:
			default:
				{
					break;
				}
		}
		break;
	}

	/* Then, check to see whether the interpretation is over.                   */
	if (((port_p->cmd_info).cmd_txt_p[(port_p->cmd_info).next_position] == (port_p->dce_info_p)->cr_character) || \
		((port_p->cmd_info).next_position >= (port_p->cmd_info).cmd_txt_length))
	{
		(port_p->cmd_info).status = FINISHED;
	}
	return (RV_OK);

} /******************* End of atp_interpret_raw_data function *******************/


/********************************************************************************
* Function name	: atp_translate_cmd_to_txt
*
* Description   : Translate a command in interpreted format to text format. Buffer 
*                 containing the command is assumed to be BTF buffer and is freed
*                 by this function. Text buffer is a BTF buffer
*
* Parameters    : cmd_type = type of the command (AT_CMD, RESULT_CODE and 
*                 UNSOLICITED_RESULT)
*                 cmd_nb = binary related code
*                 cmd_info_p = pointer on the custom command information structure
*                 mb_id = memory bank used to get the text buffer
*                 text_pp = pointer on the text chain (0-terminated)
*                 text_length_p = length of the text chain, '\x00' not included
*
* Return        : RV_OK,
*                 RV_NOT_SUPPORTED if the command is not recognized
*
* History       : 0.1 (25-August-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_translate_cmd_to_txt (T_ATP_CMD_TYPE  cmd_type,
									T_ATP_CMD_NB    cmd_nb,
									T_ATP_CMD       *cmd_info_p,
									T_RVF_MB_ID     mb_id,
									T_ATP_TXT_CMD   *text_pp,
									UINT16          *text_length_p)
{
    /* Declare local variables.                                                 */
	const char  *table     = NULL;
	UINT16      offset     = 0x0000;
	T_ATP_RET   ret_status = RV_OK;
	
/******************* atp_translate_cmd_to_txt function begins *******************/

	switch (cmd_type)
	{

		/* Translate AT commands into text.                                     */
		case AT_CMD:
			{
				rvf_send_trace ("ATP : Translate an AT command into text ",
								40,
								NULL_PARAM,
								RV_TRACE_LEVEL_DEBUG_LOW,
								ATP_USE_ID);
				table = ATP_AT_TXT_TABLE;

				/* Get the related offset.                                      */
				offset = ATP_AT_INFO[cmd_nb][ATP_AT_OFFSET_COLUMN];

				/* Get the length of the AT command.                            */
				*text_length_p = (UINT16) (ATP_AT_PREFIX_LEN + \
										   ATP_AT_INFO[cmd_nb + 0x01][ATP_AT_OFFSET_COLUMN] - ATP_AT_INFO[cmd_nb][ATP_AT_OFFSET_COLUMN]);

				/* Get the related structure.                                   */
				switch (ATP_AT_INFO[cmd_nb][ATP_AT_PARAM_COLUMN])
				{

					/* Basic syntax command does not expect any <number>.       */
					case ATP_NO_PARAM:
						{

							/* Create a buffer and copy text string into it     */
							/* ('\x00' included). If insufficient resources     */
							/* available, then report an internal memory error  */
							/* and abort.                                       */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + 0x0001, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}
	
							/* Copy the command line prefix into the buffer.    */
							memcpy ((void *) (*text_pp),
									ATP_AT_PREFIX,
									ATP_AT_PREFIX_LEN);

							/* Copy the text into the buffer.                   */
							memcpy ((void *) &((*text_pp)[ATP_AT_PREFIX_LEN]),
									(void *) &(table[offset]),
									*text_length_p - ATP_AT_PREFIX_LEN);
							(*text_pp)[*text_length_p] = '\x00';
							break;
						}

					/* Basic syntax command.                                    */
					case ATP_BASIC_PARAM:
						{

							/* Declare a local block variable.                  */
							T_ATP_BASIC_CMD  *basic_cmd_param_p = (T_ATP_BASIC_CMD *) cmd_info_p;

							/* Create a buffer and copy text string into it     */
							/* ('\x00' included). If insufficient resources     */
							/* available, then report an internal memory error  */
							/* and abort.                                       */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + MAX_BASIC_CMD_PARAM_LEN + 0x0001, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}
	
							/* Copy the command line prefix into the buffer.    */
							memcpy ((void *) (*text_pp),
									ATP_AT_PREFIX,
									ATP_AT_PREFIX_LEN);

							/* Copy the text into the buffer.                   */
							memcpy ((void *) &((*text_pp)[ATP_AT_PREFIX_LEN]),
									(void *) &(table[offset]),
									*text_length_p - ATP_AT_PREFIX_LEN);

							/* If needed, copy the buffer describing the        */
							/* command in interpreted format and free it.       */
							if (basic_cmd_param_p != NULL)
							{
								*text_length_p = (UINT16) (*text_length_p + \
														   dtoa (basic_cmd_param_p->number,
																 &((*text_pp)[*text_length_p])));
							}
							(*text_pp)[*text_length_p] = '\x00';
							break;
						}

					/* Dial.                                                    */
					case ATP_DIAL_PARAM:
						{

							/* Declare local block variables.                   */
							UINT8       dial_param_length = 0x00;
							UINT8       dial_semicolon    = DATA_CALL;
							T_ATP_DIAL  *dial_param_p     = (T_ATP_DIAL *) cmd_info_p;

							/* If needed, take the buffer describing the        */
							/* command in interpreted format into account.      */
							if (dial_param_p != NULL)
							{
								dial_param_length = dial_param_p->dial_string_length;
								dial_semicolon    = (UINT8) (dial_param_p->call_type);
							}

							/* Create a buffer and copy text string into it     */
							/* ('\x00' included). If insufficient resources     */
							/* available, then report an internal memory error  */
							/* and abort.                                       */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + dial_param_length + dial_semicolon + 0x0001, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}
	
							/* Copy the command line prefix into the buffer.    */
							memcpy ((void *) (*text_pp),
									ATP_AT_PREFIX,
									ATP_AT_PREFIX_LEN);

							/* Copy the text into the buffer.                   */
							memcpy ((void *) &((*text_pp)[ATP_AT_PREFIX_LEN]),
									(void *) &(table[offset]),
									*text_length_p - ATP_AT_PREFIX_LEN);

							/* If needed, copy the buffer describing the        */
							/* command in interpreted format and free it.       */
							if (dial_param_p != NULL)
							{
								memcpy ((void *) &((*text_pp)[*text_length_p]),
										(void *) (dial_param_p->dial_string_p),
										dial_param_length);
								if (dial_semicolon == VOICE_CALL)
								{
									(*text_pp)[*text_length_p + dial_param_length] = ';';
								}
								*text_length_p = (UINT16) (*text_length_p + \
														   dial_param_length + dial_semicolon);
							}
							(*text_pp)[*text_length_p] = '\x00';
							break;
						}

					/* S-parameter.                                             */
					case ATP_S_PARAM:
						{

							/* Declare a local block variable.                  */
							T_ATP_S_PARAM  *s_param_p = (T_ATP_S_PARAM *) cmd_info_p;

							/* Create a buffer and copy text string into it     */
							/* ('\x00' included). If insufficient resources     */
							/* available, then report an internal memory error  */
							/* and abort.                                       */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + MAX_S_PARAM_LEN + 0x0001,
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}
	
							/* Copy the command line prefix into the buffer.    */
							memcpy ((void *) (*text_pp),
									ATP_AT_PREFIX,
									ATP_AT_PREFIX_LEN);

							/* Copy the text into the buffer.                   */
							memcpy ((void *) &((*text_pp)[ATP_AT_PREFIX_LEN]),
									(void *) &(table[offset]),
									*text_length_p - ATP_AT_PREFIX_LEN);

							/* If needed, copy the buffer describing the        */
							/* command in interpreted format and free it.       */
							if (s_param_p == NULL)
							{
								(*text_pp)[*text_length_p] = '\x00';
								break;
							}
							switch (s_param_p->s_operator)
							{

								/* Parameter read command syntax.               */
								case READ_S_PARAM:
									{
										(*text_pp)[(*text_length_p)++] = '?';
										(*text_pp)[*text_length_p]     = '\x00';
										break;
									}

								/* Parameter set command syntax.                */
								case SET_S_PARAM:
									{
										(*text_pp)[(*text_length_p)++] = '=';
										*text_length_p = (UINT16) (*text_length_p + \
																   dtoa (s_param_p->value,
																		 &((*text_pp)[*text_length_p])));
										(*text_pp)[*text_length_p] = '\x00';
										break;
									}
								default:
									{
										rvf_free_buf (*text_pp);
										*text_pp   = NULL;
										ret_status = RV_NOT_SUPPORTED;
										break;
									}
							}
							break;
						}

					/* Extended syntax command does not expect any <value>.     */
					case ATP_NO_EXTENDED_PARAM:
						{

							/* Declare a local block variable.                  */
							T_ATP_NO_SUBPARAMETER  *extended_cmd_param_p = (T_ATP_NO_SUBPARAMETER *) cmd_info_p;

							/* Create a buffer and copy text string into it     */
							/* ('\x00' included). If insufficient resources     */
							/* available, then report an internal memory error  */
							/* and abort.                                       */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + MAX_NO_SUBPARAMETER_LEN + 0x0001, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}
	
							/* Copy the command line prefix into the buffer.    */
							memcpy ((void *) (*text_pp),
									ATP_AT_PREFIX,
									ATP_AT_PREFIX_LEN);

							/* Copy the text into the buffer.                   */
							memcpy ((void *) &((*text_pp)[ATP_AT_PREFIX_LEN]),
									(void *) &(table[offset]),
									*text_length_p - ATP_AT_PREFIX_LEN);

							/* If needed, copy the buffer describing the        */
							/* command in interpreted format and free it.       */
							if (extended_cmd_param_p == NULL)
							{
								(*text_pp)[*text_length_p] = '\x00';
								break;
							}
							switch (extended_cmd_param_p->extended_operator)
							{

								/* No subparameter.                             */
								case NO_SUBPARAMETER:
									{
										(*text_pp)[*text_length_p] = '\x00';
										break;
									}

								/* Action test command syntax.                  */
								case TEST_EXTENDED_CMD:
									{
										(*text_pp)[(*text_length_p)++] = '=';
										(*text_pp)[(*text_length_p)++] = '?';
										(*text_pp)[*text_length_p]     = '\x00';
										break;
									}
								default:
									{
										rvf_free_buf (*text_pp);
										*text_pp   = NULL;
										ret_status = RV_NOT_SUPPORTED;
										break;
									}
							}
							break;
						}

					/* Extended syntax command whose subparameter is a numeric  */
					/* constant.                                                */
					case ATP_SINGLE_EXTENDED_PARAM:
						{

							/* Declare a local block variable.                  */
							T_ATP_SINGLE_SUBPARAMETER  *extended_cmd_param_p = (T_ATP_SINGLE_SUBPARAMETER *) cmd_info_p;

							/* Create a buffer and copy text string into it     */
							/* ('\x00' included). If insufficient resources     */
							/* available, then report an internal memory error  */
							/* and abort.                                       */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + MAX_SINGLE_SUBPARAMETER_LEN + 0x0001, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}
	
							/* Copy the command line prefix into the buffer.    */
							memcpy ((void *) (*text_pp),
									ATP_AT_PREFIX,
									ATP_AT_PREFIX_LEN);

							/* Copy the text into the buffer.                   */
							memcpy ((void *) &((*text_pp)[ATP_AT_PREFIX_LEN]),
									(void *) &(table[offset]),
									*text_length_p - ATP_AT_PREFIX_LEN);

							/* If needed, copy the buffer describing the        */
							/* command in interpreted format and free it.       */
							if (extended_cmd_param_p == NULL)
							{
								(*text_pp)[*text_length_p] = '\x00';
								break;
							}
							switch (extended_cmd_param_p->extended_operator)
							{

								/* No subparameter.                             */
								case NO_SUBPARAMETER:
									{
										(*text_pp)[*text_length_p] = '\x00';
										break;
									}

								/* Action test command syntax.                  */
								case TEST_EXTENDED_CMD:
									{
										(*text_pp)[(*text_length_p)++] = '=';
									}

								/* Parameter read command syntax.               */
								case READ_EXTENDED_CMD:
									{
										(*text_pp)[(*text_length_p)++] = '?';
										(*text_pp)[*text_length_p]     = '\x00';
										break;
									}

								/* Parameter set command syntax.                */
								case SET_EXTENDED_CMD:
									{
										(*text_pp)[(*text_length_p)++] = '=';
										*text_length_p = (UINT16) (*text_length_p + \
																   dtoa (extended_cmd_param_p->value,
																		 &((*text_pp)[*text_length_p])));
										(*text_pp)[*text_length_p] = '\x00';
										break;
									}
								default:
									{
										rvf_free_buf (*text_pp);
										*text_pp   = NULL;
										ret_status = RV_NOT_SUPPORTED;
										break;
									}
							}
							break;
						}

					/* Keypad control command. Note that <keys> is a string of  */
					/* characters representing keys (See See ETS 300 916 (GSM   */
					/* 07.07) Version 5.8.1 page 62). Colon character followed  */
					/* by one character can be used to indicate a manufacturer  */
					/* specific key not listed here. All characters from a      */
					/* semicolon character to the next single semicolon         */
					/* character are treated as alpha entries and are not       */
					/* converted to key equivalents. All semicolon characters   */
					/* inside alpha entries should be duplicated in the DTE.    */
					/* Pause characters "W" and "w" can be used to pause        */
					/* between key pressings for a time specified by <pause>.   */
					case ATP_PLUS_CKPD_PARAM:
						{

							/* Declare local block variables.                   */
							UINT8            nb_keypressed = 0x00;
							T_ATP_PLUS_CKPD  *ckpd_param_p = (T_ATP_PLUS_CKPD *) cmd_info_p;

							/* Create a buffer and copy text string into it     */
							/* ('\x00' included). If insufficient resources     */
							/* available, then report an internal memory error  */
							/* and abort.                                       */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + MAX_CKPD_PARAM_LEN + 0x0001, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}

							/* Copy the command line prefix into the buffer.    */
							memcpy ((void *) (*text_pp),
									ATP_AT_PREFIX,
									ATP_AT_PREFIX_LEN);

							/* Copy the text into the buffer.                   */
							memcpy ((void *) &((*text_pp)[ATP_AT_PREFIX_LEN]),
									(void *) &(table[offset]),
									*text_length_p - ATP_AT_PREFIX_LEN);

							/* If needed, copy the buffer describing the        */
							/* command in interpreted format and free it.       */
							if (ckpd_param_p == NULL)
							{
								(*text_pp)[*text_length_p] = '\x00';
								break;
							}
							switch (ckpd_param_p->extended_operator)
							{

								/* Action test command syntax.                  */
								case TEST_EXTENDED_CMD:
									{
										(*text_pp)[(*text_length_p)++] = '=';
										(*text_pp)[(*text_length_p)++] = '?';
										(*text_pp)[*text_length_p]     = '\x00';
										break;
									}

								/* Parameter set command syntax.                */
								case SET_EXTENDED_CMD:
									{
										(*text_pp)[(*text_length_p)++] = '=';
										(*text_pp)[(*text_length_p)++] = '"';

										/* Store each keypressed into the       */
										/* buffer.                              */
										for (nb_keypressed = 0x00;
											 nb_keypressed < ckpd_param_p->nb_keys;
											 nb_keypressed++)
										{
											(*text_pp)[(*text_length_p)++] = ';';
											*text_length_p = (UINT16) (*text_length_p + \
																	   dtoa (ckpd_param_p->keys[nb_keypressed],
																			 &((*text_pp)[*text_length_p])));
											(*text_pp)[(*text_length_p)++] = ';';
										}
										(*text_pp)[(*text_length_p)++] = '"';

										/* Store <time> subparameter into the   */
										/* buffer.                              */
										(*text_pp)[(*text_length_p)++] = ',';
										if ((ckpd_param_p->pause != DEFAULT_TIME) || \
											(ckpd_param_p->pause != TIME_DO_NOT_CARE))
										{
											*text_length_p = (UINT16) (*text_length_p + \
																	   dtoa (ckpd_param_p->time,
																			 &((*text_pp)[*text_length_p])));
										}

										/* Store <pause> subparameter into the  */
										/* buffer.                              */
										(*text_pp)[(*text_length_p)++] = ',';
										if ((ckpd_param_p->pause != DEFAULT_PAUSE) || \
											(ckpd_param_p->pause != PAUSE_DO_NOT_CARE))
										{
											*text_length_p = (UINT16) (*text_length_p + \
																	   dtoa (ckpd_param_p->pause,
																			 &((*text_pp)[*text_length_p])));
										}
										(*text_pp)[*text_length_p] = '\x00';
										break;
									}
								default:
									{
										rvf_free_buf (*text_pp);
										*text_pp   = NULL;
										ret_status = RV_NOT_SUPPORTED;
										break;
									}
							}
							break;
						}
					default:
						{
							rvf_send_trace ("ATP : Received an unknown command ",
											34,
											NULL_PARAM,
											RV_TRACE_LEVEL_WARNING,
											ATP_USE_ID);
							*text_pp   = NULL;
							ret_status = RV_NOT_SUPPORTED;
							break;
						}
				}
				break;
			}

		/* Translate DCE responses into text (See ITU-T Recommendation V.250    */
		/* ter page 10).                                                        */
		case RESULT_CODE:
		case UNSOLICITED_RESULT:
			{
				rvf_send_trace ("ATP : Translate a result into text ",
								35,
								NULL_PARAM,
								RV_TRACE_LEVEL_DEBUG_LOW,
								ATP_USE_ID);
				table = ATP_RESULT_CODE_TXT_TABLE_V1;

				/* Get the related offset.                                      */
				offset = ATP_RESULT_CODE_INFO[cmd_nb][ATP_RESULT_OFFSET_V1_COLUMN];

				/* Get the length of the result code.                           */
				*text_length_p = (UINT16) (ATP_RESULT_CODE_INFO[cmd_nb + 0x01][ATP_RESULT_OFFSET_V1_COLUMN] - ATP_RESULT_CODE_INFO[cmd_nb][ATP_RESULT_OFFSET_V1_COLUMN]);

				/* Get the related structure.                                   */
				switch (ATP_RESULT_CODE_INFO[cmd_nb][ATP_RESULT_PARAM_COLUMN])
				{

					/* Basic syntax result code.                                */
					case ATP_BASIC_RESULT_CODE:
						{

							/* Create a buffer and copy text string into it     */
							/* ('\x00' included). If insufficient resources     */
							/* available, then report an internal memory error  */
							/* and abort.                                       */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + 0x0001, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}

							/* Copy text into the buffer.                       */
							memcpy ((void *) (*text_pp),
									(void *) &(table[offset]),
									*text_length_p);
							(*text_pp)[*text_length_p] = '\x00';
							break;
						}

					/* Extended syntax result code.                             */
					case ATP_EXTENDED_RESULT_CODE:
						{

							/* Declare a local block variable.                  */
							T_ATP_SINGLE_RESULT_CODE_VALUE  *result_code_param_p = (T_ATP_SINGLE_RESULT_CODE_VALUE *) cmd_info_p;

							/* Create a buffer and copy text string into it     */
							/* ('=' and '\x00' included). If insufficient       */
							/* resources available, then report an internal     */
							/* memory error and abort.                          */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + MAX_SINGLE_RESULT_CODE_VALUE_LEN + 0x0002, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}

							/* Copy text into the buffer.                       */
							memcpy ((void *) (*text_pp),
									(void *) &(table[offset]),
									*text_length_p);

							/* If needed, copy the buffer describing the        */
							/* command in interpreted format and free it.       */
							if (result_code_param_p != NULL)
							{
								(*text_pp)[(*text_length_p)++] = '=';
								*text_length_p = (UINT16) (*text_length_p + \
														   dtoa (result_code_param_p->value,
																 &((*text_pp)[*text_length_p])));
							}
							(*text_pp)[*text_length_p] = '\x00';
							break;
						}

					/* CONNECT <text> result code.                              */
					case ATP_CONNECT_TXT_PARAM:
						{

							/* Declare a local block variable.                  */
							T_ATP_CONNECT_TXT_PARAM  *connect_txt_param_p = (T_ATP_CONNECT_TXT_PARAM *) cmd_info_p;

							/* Create a buffer and copy text string into it     */
							/* (' ' and '\x00' included). If insufficient       */
							/* resources available, then report an internal     */
							/* memory error and abort.                          */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + MAX_CONNECT_TXT_LEN + 0x0002, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}

							/* Copy text into the buffer.                       */
							memcpy ((void *) (*text_pp),
									(void *) &(table[offset]),
									*text_length_p);

							/* If needed, copy the buffer describing the        */
							/* command in interpreted format and free it.       */
							if (connect_txt_param_p != NULL)
							{
								(*text_pp)[(*text_length_p)++] = ' ';
								*text_length_p = (UINT16) (*text_length_p + \
														   dtoa (connect_txt_param_p->value,
																 &((*text_pp)[*text_length_p])));
							}
							(*text_pp)[*text_length_p] = '\x00';
							break;
						}

					/* +CME ERROR: <error> and +CMS ERROR: <error> result       */
					/* codes.                                                   */
					case ATP_PLUS_ERROR_RESULT_CODE:
						{

							/* Declare local block variables.                   */
							UINT8                         error_length              = 0x00;
							T_ATP_PLUS_ERROR_RESULT_CODE  *plus_error_result_code_p = cmd_info_p;

							/* If needed, take the buffer describing the        */
							/* command in interpreted format into account.      */
							if (plus_error_result_code_p != NULL)
							{
								error_length = plus_error_result_code_p->error_length;
							}

							/* Create a buffer and copy text string into it     */
							/* (' ' and '\x00' included). If insufficient       */
							/* resources available, then report an internal     */
							/* memory error and abort.                          */
							if (rvf_get_buf (mb_id, \
											 *text_length_p + error_length + 0x0002, \
											 (void **) text_pp) == RVF_RED)
							{
								atp_error (ATP_ERROR_TX_MB_RED);
								ret_status = RV_MEMORY_ERR;
								break;
							}
	
							/* Copy the command line prefix into the buffer.    */
							memcpy ((void *) (*text_pp),
									ATP_AT_PREFIX,
									ATP_AT_PREFIX_LEN);

							/* Copy the text into the buffer.                   */
							memcpy ((void *) &((*text_pp)[ATP_AT_PREFIX_LEN]),
									(void *) &(table[offset]),
									*text_length_p - ATP_AT_PREFIX_LEN);

							/* If needed, copy the buffer describing the        */
							/* command in interpreted format and free it.       */
							if (plus_error_result_code_p != NULL)
							{
								(*text_pp)[(*text_length_p)++] = ' ';
								memcpy ((void *) &((*text_pp)[*text_length_p]),
										(void *) (plus_error_result_code_p->error_p),
										error_length);
								*text_length_p = (UINT16) (*text_length_p + \
														   error_length);
							}
							(*text_pp)[*text_length_p] = '\x00';
							break;
						}
					default:
						{
							rvf_send_trace ("ATP : Received an unknown command ",
											34,
											NULL_PARAM,
											RV_TRACE_LEVEL_WARNING,
											ATP_USE_ID);
							*text_pp   = NULL;
							ret_status = RV_NOT_SUPPORTED;
							break;
						}
				}
				break;
			}
		default:
			{
				rvf_send_trace ("ATP : Received an unknown command ",
								34,
								NULL_PARAM,
								RV_TRACE_LEVEL_WARNING,
								ATP_USE_ID);
				*text_pp   = NULL;
				ret_status = RV_NOT_SUPPORTED;
				break;
			}
	}

	/* If needed, free the buffer describing the command in interpreted format. */
	if (cmd_info_p != NULL)
	{
		rvf_free_buf (cmd_info_p);
		cmd_info_p = NULL;
	}
	return (ret_status);

} /****************** End of atp_translate_cmd_to_txt function ******************/


/********************************************************************************
* Function name : atp_copy_buffer
* 
* Description   : Copy 'data size' bytes of 'in buffer' to 'out buffer'
*
* Parameters    : in_buffer_p = buffer to copy from
*                 out_buffer_p = new buffer
*                 data_size = number of bytes to be copied
*
* Return        : RV_OK
*
* History       : 0.1 (05-May-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_copy_buffer (UINT8   *in_buffer_p,
						   UINT8   *out_buffer_p,
						   UINT32  data_size)
{

/************************ atp_copy_buffer function begins ***********************/

	memcpy ((void *) out_buffer_p,
			(void *) in_buffer_p,
			data_size);
  	return (RV_OK);

} /*********************** End of atp_copy_buffer function **********************/


/********************************************************************************
* Function name : atp_create_data_buffer_from_cmd
* 
* Description   : create a data buffer based on port features and on the command 
*                 which is interpreted or in text format
*
* Parameters    : cmd_mode = interpreted or text
*                 header = header size
*                 trailer = trailer size
*                 dce_info_p = pointer on the DCE information
*                 mb_id = memory bank used to get the data buffer
*                 cmd_type = type of the command
*                 cmd_nb = related binary code of the command (not used if TXT
*                          format)
*                 text_p = pointer on the text string (0-terminated) (not used
*                          if INTERPRETED format)
*                 cmd_info_p = pointer on the custom command information structure
*                              (not used if TXT format)
*                 buffer_pp = pointer on the data buffer generated by the function
*                 length_p = length of the data buffer
*
* Return        : RV_OK,
*                 RV_NOT_SUPPORTED if the command is not recognized
*
* History       : 0.1 (01-March-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_create_data_buffer_from_cmd (T_ATP_CMD_MODE  cmd_mode,
										   UINT16          header,
										   UINT16          trailer,
										   T_ATP_DCE_INFO  *dce_info_p,
										   T_RVF_MB_ID     mb_id,
										   T_ATP_CMD_TYPE  cmd_type,
										   T_ATP_CMD_NB    cmd_nb,
										   T_ATP_TXT_CMD   text_p,
										   T_ATP_CMD       *cmd_info_p,
										   T_ATP_BUFFER    *buffer_pp,
										   UINT16          *length_p)
{
    /* Declare local variables.                                                 */
	UINT16         txt_length   = 0x0000;
	T_ATP_BUFFER   atp_buffer_p = NULL;
	T_ATP_TXT_CMD  text_cmd_p   = NULL;
	
/**************** atp_create_data_buffer_from_cmd function begins ***************/

	/* Need to have the command type provided.                                  */
	if (cmd_type == UNKNOWN)
	{
		return (RV_NOT_SUPPORTED);
	}

	/* Get text version of the AT command and point on it via text_cmd_p. If    */
	/* the command has been provided already in text format, then...            */
	if (cmd_mode == TXT_MODE)
	{
		text_cmd_p = text_p;

		/* The length does not include '\x00'.                                  */
		txt_length = (UINT16) (strlen (text_cmd_p));
	}
	else
	{

		/* Declare a local block variable.                                      */
		T_ATP_RET  ret_status = RV_OK;

		/* Translate the command into text.                                     */
		ret_status = atp_translate_cmd_to_txt (cmd_type,
											   cmd_nb,
											   cmd_info_p,
											   mb_id,
											   &text_cmd_p,
											   &txt_length);

		/* If any error occurred, then abort.                                   */
		if (ret_status != RV_OK)
		{
			return (RV_NOT_SUPPORTED);
		}
	}

	/* Create real buffer to send for a AT Command.                             */
	switch (dce_info_p->verbose_mode)
	{

		/* Verbose responses enabled.                                           */
		case ATP_VERBOSE_0:
			{
				switch (cmd_type)
				{

					/* DCE responses (See ITU-T Recommendation V.250 ter page   */
					/* 10).                                                     */
					case RESULT_CODE:
					case UNSOLICITED_RESULT:
						{
							*length_p = (UINT16) (trailer + header + 0x0002);

							/* Create buffer and copy text string into it. If   */
							/* insufficient resources available, then report an */
							/* internal memory error and abort.                 */
							if (rvf_get_buf (mb_id, \
											 *length_p, \
											 (void **) buffer_pp) == RVF_RED)
							{
								*length_p = 0x0000;
								atp_error (ATP_ERROR_TX_MB_RED);
								return (RV_MEMORY_ERR);
							}
							atp_buffer_p = *buffer_pp;

							/* Add code.                                        */
							atp_buffer_p[header] = (char) (cmd_nb + '0');

							/* Add special characters.                          */
							atp_buffer_p[header + 0x0001] = dce_info_p->cr_character;

							/* Release text buffer.                             */
							rvf_free_buf (text_cmd_p);
							return (RV_OK);
						}
					case PRELIMINARY_RESULT_CODE:
						{

							/* Add 2 special characters <CR> and <LF>.          */
							*length_p = (UINT16) (trailer + header + txt_length + 0x0002);
			
							/* Create buffer and copy text string into it. If   */
							/* insufficient resources available, then report an */
							/* internal memory error and abort.                 */
							if (rvf_get_buf (mb_id, \
											 *length_p, \
											 (void **) buffer_pp) == RVF_RED)
							{
								*length_p = 0x0000;
								atp_error (ATP_ERROR_TX_MB_RED);
								return (RV_MEMORY_ERR);
							}
							atp_buffer_p = *buffer_pp;

							/* Copy text into the buffer.                       */
							memcpy ((void *) (atp_buffer_p + header),
									(void *) text_cmd_p,
									txt_length);

							/* Add special characters.                          */
							atp_buffer_p[header + txt_length]          = dce_info_p->cr_character;
							atp_buffer_p[header + txt_length + 0x0001] = dce_info_p->lf_character;

							/* Release text buffer.                             */
							rvf_free_buf (text_cmd_p);
							return (RV_OK);
						}
					default:
						{
							break;
						}
				}
			}

		/* Verbose responses disabled.                                          */
		case ATP_VERBOSE_1:
			{
				switch (cmd_type)
				{

					/* DTE command lines (See ITU-T Recommendation V.250 ter    */
					/* page 4).                                                 */
					case AT_CMD:
						{

							/* The buffer contains AT command and <CR>          */
							/* character.                                       */
							*length_p = (UINT16) (trailer + header + txt_length + 0x0001);

							/* Create buffer and copy text string into it. If   */
							/* insufficient resources available, then report an */
							/* internal memory error and abort.                 */
							if (rvf_get_buf (mb_id, \
											 *length_p, \
											 (void **) buffer_pp) == RVF_RED)
							{
								*length_p = 0x0000;
								atp_error (ATP_ERROR_TX_MB_RED);
								return (RV_MEMORY_ERR);
							}
							atp_buffer_p = *buffer_pp;

							/* Copy text into the buffer.                       */
							memcpy ((void *) (atp_buffer_p + header),
									(void *) text_cmd_p,
									txt_length);

							/* Add special characters.                          */
							atp_buffer_p[header + txt_length] = dce_info_p->cr_character;

							/* Release text buffer.                             */
							rvf_free_buf (text_cmd_p);
							return (RV_OK);
						}

					/* DCE responses (See ITU-T Recommendation V.250 ter page   */
					/* 10).                                                     */
					case RESULT_CODE:
					case UNSOLICITED_RESULT:
					case PRELIMINARY_RESULT_CODE:
						{

							/* Add 4 special characters <CR> and <LF> (twice).  */
							*length_p = (UINT16) (trailer + header + txt_length + 0x0004);
			
							/* Create buffer and copy text string into it. If   */
							/* insufficient resources available, then report an */
							/* internal memory error and abort.                 */
							if (rvf_get_buf (mb_id, \
											 *length_p, \
											 (void **) buffer_pp) == RVF_RED)
							{
								*length_p = 0x0000;
								atp_error (ATP_ERROR_TX_MB_RED);
								return (RV_MEMORY_ERR);
							}
							atp_buffer_p = *buffer_pp;

							/* Copy text into the buffer.                       */
							memcpy ((void *) (atp_buffer_p + header + 0x0002),
									(void *) text_cmd_p,
									txt_length);

							/* Add special characters.                          */
							atp_buffer_p[header]                       = dce_info_p->cr_character;
							atp_buffer_p[header + 0x0001]              = dce_info_p->lf_character;
							atp_buffer_p[header + txt_length + 0x0002] = dce_info_p->cr_character;
							atp_buffer_p[header + txt_length + 0x0003] = dce_info_p->lf_character;

							/* Release text buffer.                             */
							rvf_free_buf (text_cmd_p);
							return (RV_OK);
						}
					default:
						{
							rvf_send_trace ("ATP : Tried to create a buffer for an unknown command ",
											54,
											NULL_PARAM,
											RV_TRACE_LEVEL_WARNING,
											ATP_USE_ID);
							*length_p  = 0x0000;
							*buffer_pp = NULL;
							break;
						}
				}
				break;
			}
		default:
			{
				rvf_send_trace ("ATP : Verbose mode invalid ",
								27,
								NULL_PARAM,
								RV_TRACE_LEVEL_WARNING,
								ATP_USE_ID);
				*length_p  = 0x0000;
				*buffer_pp = NULL;
				break;
			}
	}
	return (RV_NOT_SUPPORTED);

} /*************** End of atp_create_data_buffer_from_cmd function **************/


/********************************************************************************
* Function name : atp_copy_buffer_from_l2cap
*
* Description   : Check the text command
*
* Parameters    : l2cap_buffer_p = type is L2CAP_ACL_DATA
*                 copy_buffer_p = pointer on the buffer to copy data in 
*                 buffer_length = data length to read
*
* Return        : RV_OK if it fits
*
* Note          : l2cap_buffer_p is not freed by this function !
*
* History       : 0.1 (21-March-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_copy_buffer_from_l2cap (void    *l2cap_buffer_p,
									  void    *copy_buffer_p,
									  UINT32  buffer_length,
									  UINT32  offset)
{

#ifdef BLUETOOTH

	/* Declare local variables.                                                 */
	UINT16            nb_byte    = 0x0000;
	T_RECV_DATA_ADDR  acl_data_p = {NULL, \
									NULL};
	
/****************** atp_copy_buffer_from_l2cap function begins ******************/

	rvf_send_trace ("ATP : Translate L2CAP buffer into a ATP buffer ",
					47,
					NULL_PARAM,
					RV_TRACE_LEVEL_DEBUG_LOW,
					ATP_USE_ID);
	l2cap_read_next_uint ((T_L2CAP_ACL_DATA *) l2cap_buffer_p,
						  sizeof (UINT8),
						  ((UINT8 *) copy_buffer_p),
						  &acl_data_p,
						  (UINT8) offset);
	for (nb_byte = 1;
		 nb_byte < buffer_length;
		 nb_byte++)
	{
		l2cap_read_next_uint ((T_L2CAP_ACL_DATA *) l2cap_buffer_p,
							  sizeof (UINT8),
							  ((UINT8 *) copy_buffer_p) + nb_byte,
							  &acl_data_p,
							  0x0000);
	}
	return (RV_OK);
#else
	rvf_send_trace ("ATP : Tried to read buffer in L2CAP format whereas Bluetooth is not enabled ",
					76,
					NULL_PARAM,
					RV_TRACE_LEVEL_WARNING,
					ATP_USE_ID);
	return (RV_NOT_SUPPORTED);
#endif
	
} /***************** End of atp_copy_buffer_from_l2cap function *****************/


/********************************************************************************
* Function name : atp_free_l2cap_buffer
*
* Description   : Release a L2CAP buffer
*
* Parameter     : l2cap_buffer_p = type is L2CAP_ACL_DATA
*
* Return        : RV_OK if free is OK
*
* History       : 0.1 (19-Dec-2001) - Created
*
*********************************************************************************/
T_ATP_RET atp_free_l2cap_buffer (UINT8  *atp_buffer_p)
{
	
/********************* atp_free_l2cap_buffer function begins ********************/

#ifdef BLUETOOTH
	return ((T_ATP_RET) (l2cap_free_data ((T_L2CAP_ACL_DATA *) atp_buffer_p)));
#else
	rvf_send_trace ("ATP : Tried to read buffer in L2CAP format whereas Bluetooth is not enabled ",
					76,
					NULL_PARAM,
					RV_TRACE_LEVEL_WARNING,
					ATP_USE_ID);
	return (RV_NOT_SUPPORTED);
#endif

} /******************** End of atp_free_l2cap_buffer function *******************/


/********************************************************************************
* Function name : atp_escape_sequence_process
*
* Description   : This function is used to detect the escape sequence in the data
*                 flow. This function should not be called if the port is not
*                 configured in DCE mode. The escape sequence should start in a new
*                 packet and the last character of the exit sequence should be the
*                 last character of a packet. Note that each data buffer checked
*                 that may be part of the escape sequence is temporarily stored so
*                 that it can be sent to the SWE later on in case it was the escape
*                 sequence.
*
* Parameters	: port_p = structure of the port
*                 atp_buffer_p = pointer on the buffer received by ATP (can be a 
*                 NORMAL data packet or a SEGMENTED data packet)
*                 data_length = number of payload data in the packet pointed by
*                 atp_buffer_p
*                 packet_mode = indicates the mode of the data packet (NORMAL or
*                 SEGMENTED)
*
* Return        : ATP_ESCAPE_SEQUENCE_SUCCESS,
*                 ATP_ESCAPE_SEQUENCE_FAILED otherwise
*
* History       : 0.1 (06-Feb-2001) - Created
*
*********************************************************************************/
T_ATP_ESCAPE_SEQUENCE_STATUS atp_escape_sequence_process (T_ATP_PORT_STRUCT  *port_p,
														  UINT8              *atp_buffer_p,
														  UINT32             data_length,
														  T_ATP_PACKET_MODE  packet_mode)
{
	/* Declare local variables.                                                 */
	UINT8  count   = 0;
	UINT8  data_sequence[MAX_NB_OF_CHARACTER_FOR_END_SEQUENCE];
	
/****************** atp_escape_sequence_process function begins *****************/

	/* Check the sequence. Indeed, there are data after the last character of   */
	/* the escape sequence.                                                     */
	if (((port_p->dce_info_p)->nb_plus_received + data_length) > MAX_NB_OF_CHARACTER_FOR_END_SEQUENCE)
	{
		return (ATP_ESCAPE_SEQUENCE_FAILED);
	}

	/* Get data from the buffer.                                                */
	if (packet_mode == SEGMENTED_PACKET)
	{
		atp_copy_buffer_from_l2cap (atp_buffer_p,
									data_sequence,
									data_length,
									0);
	}
	else
	{
		memcpy (data_sequence,
				atp_buffer_p,
				data_length);
	}

	/* Check every character.                                                   */
	for (count = 0;
		 count < data_length;
		 count++)
	{
		if (data_sequence[count] != (port_p->dce_info_p)->escape_sequence[count + (port_p->dce_info_p)->nb_plus_received])
		{
			return (ATP_ESCAPE_SEQUENCE_FAILED);
		}
	}

	/* Keep temporarily the pointer on the buffer.                              */
	for (count = 0;
		 (port_p->dce_info_p)->escape_sequence_tmp_buffer_p[count] != NULL;
		 count++);
	(port_p->dce_info_p)->escape_sequence_tmp_buffer_p[count]           = atp_buffer_p;
	(port_p->dce_info_p)->length_of_escape_sequence_tmp_buffer_p[count]	= (UINT8) data_length;
	if (((port_p->dce_info_p)->nb_plus_received + data_length) !=  MAX_NB_OF_CHARACTER_FOR_END_SEQUENCE)
	{
		(port_p->dce_info_p)->nb_plus_received = (UINT8) ((port_p->dce_info_p)->nb_plus_received + data_length);		
		return (ATP_ESCAPE_SEQUENCE_WAIT);
	}

	/* Otherwise, all characters have been found.                               */
	return (ATP_ESCAPE_SEQUENCE_SUCCESS);

} /***************** End of atp_escape_sequence_process function ****************/

  
/********************************************************************************
* Function name : atp_pipe_extra_character
*
* Description   : This function is called when it has been found that the escape
*                 sequence search has failed and when other SWE is in copy mode.
*                 In this case, data that has been received and that have not been
*                 sent to the other SWE because we thought they may contain the
*                 escape sequence must finally be sent.
*                 For this reason, all the buffer to sent has been previously stored
*                 in a temporary buffer called dce_info_p->escape_sequence_tmp_buffer_p.
*                 This function pipes all this buffer into the data pipe of the SWE.
*                 Note that such a complex mechanism is essentially due to the fact
*                 that we may have to deal with L2CAP packet (SEGMENTED_MODE).
*
* Parameters    : port_p = pointer on the port structure
*                 other_port_info_p = pointer on the port information structure of
*                 the SWE which will receive the data.
*
* Return        : RV_OK
*
* History       : 0.1 (01-March-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_pipe_extra_character (T_ATP_PORT_STRUCT      *port_p,
									T_ATP_PORT_END_STRUCT  *other_port_info_p)
{
	/* Declare local variables.                                                 */
	UINT8            count        = 0;
	UINT8            nb_packet    = 0;
	T_ATP_RX_PACKET  *rx_packet_p = NULL;
	
/******************* atp_pipe_extra_character function begins *******************/

	for (nb_packet = 0;
		 ((port_p->dce_info_p)->escape_sequence_tmp_buffer_p[nb_packet] != NULL) && \
		 (nb_packet < MAX_NB_OF_CHARACTER_FOR_END_SEQUENCE);
		 nb_packet++);
	for (count = 0; count < nb_packet; count++)
	{
		if (rvf_get_buf (other_port_info_p->rx_mb, \
						 sizeof (T_ATP_RX_PACKET), \
						 (void **) &rx_packet_p) == RVF_RED)
		{
			atp_error_switch (ATP_ERROR_FAILED_TO_SEND_DATA,
							  ATP_MEMORY_ERROR,NULL);
			return (RV_MEMORY_ERR);
		}
		rx_packet_p->first_byte        = 0;
		rx_packet_p->last_byte         = ((port_p->dce_info_p)->length_of_escape_sequence_tmp_buffer_p[count]) - 1;
		rx_packet_p->atp_buffer_p      = (port_p->dce_info_p)->escape_sequence_tmp_buffer_p[count];
		rx_packet_p->next_byte_to_read = rx_packet_p->first_byte;
		rvf_enqueue (&(other_port_info_p->rx_queue),
					 rx_packet_p);
		other_port_info_p->rx_data_left += (port_p->dce_info_p)->length_of_escape_sequence_tmp_buffer_p[count];
	}
	return (RV_OK);

} /****************** End of atp_pipe_extra_character function ******************/

  
/********************************************************************************
* Function name : atp_reset_escape_sequence
*
* Description   : This function resets all internal values used by the algorithm
*                 of escape sequence search (information stored in the dce_info_p
*                 structure)
*
* Parameter     : port_p = pointer on the port structure
*
* Return        : RV_OK
*
* History       : 0.1 (01-March-2000) - Created
*
*********************************************************************************/
T_ATP_RET atp_reset_escape_sequence (T_ATP_PORT_STRUCT  *port_p)
{
	/* Declare local variables.                                                 */
	UINT8  count     = 0;
	UINT8  *buffer_p = NULL;
	
/******************* atp_reset_escape_sequence function begins ******************/

	(port_p->dce_info_p)->nb_plus_received = 0;
	
	/* Clear pointers on temporary buffer containing potentially escape         */
	/* sequence.                                                                */
	for (count = 0;
		 count < MAX_NB_OF_CHARACTER_FOR_END_SEQUENCE;
		 count++)
	{
		if ((buffer_p = (port_p->dce_info_p)->escape_sequence_tmp_buffer_p[count]) != NULL)
		{

#ifdef BLUETOOTH

			/* Check if the buffer has been issued by SPP. Not very clean way to */
			/* check what is the packet mode.                                    */
			if ((strcmp ((char *) atp_sw_entity_table_p[(port_p->port_info[0]).sw_id]->sw_entity_name, \
						 ATP_SPP_NAME) == 0) || \
				(strcmp ((char *) atp_sw_entity_table_p[(port_p->port_info[1]).sw_id]->sw_entity_name, \
						 ATP_SPP_NAME) == 0))
			{

				/* Mode is SEGMENTED PACKET.                                     */
				l2cap_free_data ((T_L2CAP_ACL_DATA *) buffer_p);
			}
			else
			{

				/* Mode  is NORMAL_PACKET.                                       */
				rvf_free_buf (buffer_p);
			}
#else
			rvf_free_buf (buffer_p);
#endif
			(port_p->dce_info_p)->escape_sequence_tmp_buffer_p[count]           = NULL;
			(port_p->dce_info_p)->length_of_escape_sequence_tmp_buffer_p[count] = 0;
		}
	}
	return (RV_OK);

} /****************** End of atp_reset_escape_sequence function ******************/