view src/cs/services/atp/atp_services.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_services.c
*
* Functions gathering all the functions defined in BT9901
*
* (C) Texas Instruments, all rights reserved
*
* Version number	: 0.1      Date : 28-Feb-2000
*					  0.9      Updated and reviewed : 10-May-2000
*
* History			: 0.1  - Created by E. Baissus
*
*
* 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_messages.h"
#include "atp/atp_cmd.h"
#include "rvm/rvm_use_id_list.h"

#include <string.h>

#define ATP_DEBUG_MSG_ENABLED
//#define ATP_HEX_DUMP_ENABLED

#ifdef ATP_DEBUG_MSG_ENABLED
	#include <stdio.h>

	static char  gbl_debug_message[100] = "";
#endif

static T_ATP_RET atp_interpret_data(T_ATP_PORT_STRUCT	*port_p,T_ATP_SW_NB spp_sw_nb, 
									T_ATP_SW_NB appli_sw_nb);

static void atp_mb_call_back(T_RVF_MB_ID mb);


#ifdef ATP_HEX_DUMP_ENABLED

/******************************************************************************
* Function name: atp_sprint_int_as_hex
*
* Description : Convert bytes to hexadecimal strings.
*
* Return     :	Length.
*
******************************************************************************/
int atp_sprint_int_as_hex(char *buf, unsigned int n, int width, char padding)
{
    unsigned int m = n; // MUST be unsigned because it will be right shifted
    int size = 0;
    int i;
    char digit;
    char *buf_start = buf;

    // Count number of digits in <n>
    do {
        size++;
    } while (m >>= 4);

    // Shift significant part of <n> into the top-most bits
    n <<= 4 * (8 - size);

    // Pad output buffer with <padding>
    if (0 < width && width <= 8) {
        width = (width > size ? width - size : 0);
        while (width--)
            *buf++ = padding;
    }

    // Convert <n>, outputting the hex digits
    for (i = 0; i < size; i++) {
        digit  = (n >> 28) & 0xF;
        digit += (digit < 10 ? '0' : 'A' - 10);
        *buf++ = digit;
        n <<= 4;
    }

    // Null terminate
    *buf = 0;

    return buf - buf_start;
}


/******************************************************************************
* Function name: atp_hexdump_buf
*
* Description : Display buffers as hexadecimal strings.
*
* Return     :	None.
*
******************************************************************************/
void atp_hexdump_buf(char *buf, int size)
{
    int n, i, multiline;
    char string[(8+1) + (16+1) + (3<<4) + 1];
    char *s;
    
    multiline = (size > 16);

    while (size > 0)
    {
        s = string;
        n = (size > 16 ? 16 : size);

        // Print address
        if (multiline) {
            s += atp_sprint_int_as_hex(s, (unsigned int) buf, 8, ' ');
            *s++ = ' ';
        }

        // Print the textual representation
        for (i = 0; i < n; i++)
            *s++ = (buf[i] >= ' ' && buf[i] < 127 ? buf[i] : '.');

        // Pad textual representation with spaces
        if (multiline)
            for (i = 0; i < 16 - n; i++)
                *s++ = ' ';

        // Print hexedecimal bytes
        for (i = 0; i < n; i++) {
            *s++ = ' ';
            s += atp_sprint_int_as_hex(s, (unsigned int) buf[i] & 0xFF, 2, '0');
        }

        *s = 0;
		rvf_send_trace (string,
						strlen (string),
						NULL_PARAM,
						RV_TRACE_LEVEL_DEBUG_LOW,
						ATP_USE_ID);
        buf  += 16;
        size -= 16;
    }
}
#endif

/******************************************************************************
* Function name: atp_open_port_rqst
*
* Description : Initialise a port creation
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id (atrget or initiator was wrong)
*					RV_MEMORY_ERR : not enough memory in ATP PRIM MB to create a new port
*
* History			: 0.1 (1-Marsh-2000) - Created
*					: 0.9  (5-May-2000) - Reviewed
******************************************************************************/
T_ATP_RET atp_open_port_rqst(T_ATP_SW_ENTITY_ID initiator_id, T_ATP_SW_ENTITY_ID target_id, 
							 T_ATP_PORT_NB port_nb, T_ATP_PORT_INFO port_info,
							 T_ATP_NO_COPY_INFO no_copy_info, T_ATP_CUSTOM_INFO * cust_info_p)
{
	T_ATP_PORT_STRUCT       *new_port_p        = NULL;
	T_ATP_OPEN_PORT_IND     *open_port_event_p = NULL;
	T_ATP_SW_ENTITY_STRUCT  *initiator_p       = NULL;
	T_ATP_SW_ENTITY_STRUCT  *target_p          = NULL;

	/* Check for invalid parameters and get the pointers on the structures that */
	/* gather information about the initiator and target SW entities */
	if ((initiator_id > ATP_MAX_NB_SW_ENTITY) || \
		(target_id > ATP_MAX_NB_SW_ENTITY) || \
		((initiator_p = atp_sw_entity_table_p[initiator_id]) == NULL) || \
		((target_p = atp_sw_entity_table_p[target_id]) == NULL))
	{
		atp_error_switch (ATP_ERROR_FAILED_TO_OPEN_A_PORT,
						  ATP_PARAM_ERROR,
						  NULL);
		return (RV_INVALID_PARAMETER);
	}

	/* Then, check whether ATP is started */
	if (atp_swe_state != ATP_STARTED)
	{
		atp_error_switch (ATP_ERROR_FAILED_TO_OPEN_A_PORT,
						  ATP_ISSUED_IN_A_WRONG_STATE_ERROR,
						  NULL);
		return (RV_NOT_SUPPORTED);
	}

	/* Create an instance gathering the port information and initialise it */
	/* (refer to atp_init_port ()) */
	if (atp_create_port (&new_port_p) != RV_OK)
	{ 
		atp_error_switch (ATP_ERROR_FAILED_TO_OPEN_A_PORT,
						  ATP_MEMORY_ERROR,
						  NULL);
		return (RV_MEMORY_ERR);
	}

	/* Increase the number of port currently in use */
	initiator_p->nb_open_port++;
	target_p->nb_open_port++;
	
	/* Update information about the initiator */
	new_port_p->cmd_info.state             = READY_FOR_NEW_CMD;
	new_port_p->port_state                 = ATP_OPEN_PENDING;
	(new_port_p->port_info[0]).sw_id       = initiator_id;
	(new_port_p->port_info[0]).port_nb     = port_nb;
	(new_port_p->port_info[0]).ring_type   = port_info.ring_type;
	(new_port_p->port_info[0]).signal_mask = (T_ATP_SIGNAL_MASK) (port_info.signal_mask & (ATP_ALL_THE_SIGNAL_UNMASK));

	/* Update information about the target */
	(new_port_p->port_info[1]).sw_id = target_id;

	/* Store the requested port configuration */
	if ((initiator_p->mode).cmd_support_mode == CMD_SUPPORT_OFF)
	{

		/* Check for invalid configuration */
		if (port_info.port_config > NOT_DEFINED_CONFIG)
		{
			atp_error_switch (ATP_ERROR_FAILED_TO_OPEN_A_PORT,
							  ATP_PARAM_ERROR,
							  NULL);

			/* Withdraw the instance gathering the port information */
			atp_delete_port (new_port_p);
				
			/* Decrease number of port currently in use */
			initiator_p->nb_open_port--;
			target_p->nb_open_port--;
			return (RV_INVALID_PARAMETER);
		}
		new_port_p->port_config = port_info.port_config;
	}

	/* Get settings related the COPY_OFF mode */
	if ((initiator_p->mode).cp_mode == COPY_OFF)
	{

		/* Get the number of bytes left free for the RX header and trailer */
		if (no_copy_info.rx_head_mode == RX_HEADER_ON)
		{
			((new_port_p->port_info[0]).no_copy_info).rx_head_size  = no_copy_info.rx_head_size;
			((new_port_p->port_info[0]).no_copy_info).rx_trail_size = no_copy_info.rx_trail_size;
		}

		/* Get the number of bytes left free for the TX header and trailer */
		if (no_copy_info.tx_head_mode == TX_HEADER_ON)
		{
			((new_port_p->port_info[0]).no_copy_info).tx_head_size  = no_copy_info.tx_head_size;
			((new_port_p->port_info[0]).no_copy_info).tx_trail_size = no_copy_info.tx_trail_size;
		}

		/* Get memory bank information */
		(new_port_p->port_info[0]).tx_mb = no_copy_info.tx_mb;
		(new_port_p->port_info[0]).rx_mb = no_copy_info.rx_mb;

		/* Copy those information on both sides. Of course TX -> RX and RX -> TX */
		(new_port_p->port_info[1]).tx_mb = no_copy_info.rx_mb;
		(new_port_p->port_info[1]).rx_mb = no_copy_info.tx_mb;

		/* Get the packet mode (i.e. SEGMENTED_PACKET) */
		((new_port_p->port_info[0]).no_copy_info).packet_mode = no_copy_info.packet_mode;
	}
	
	/* At last, send an ATP_OPEN_PORT_IND event to the target */
	switch (rvf_get_buf (atp_mb_prim, \
						 sizeof (T_ATP_OPEN_PORT_IND), \
						 (T_RVF_BUFFER **) &open_port_event_p))
	{
		case RVF_GREEN:
			{
				(open_port_event_p->rv_hdr).msg_id   = ATP_OPEN_PORT_IND;
				open_port_event_p->initiator_id      = initiator_id;
				open_port_event_p->initiator_port_nb = port_nb;
				open_port_event_p->custom_info_p     = cust_info_p;
				break;
			}

		/* Insufficient resources */
		case RVF_YELLOW:
			{
				rvf_free_buf ((T_RVF_BUFFER *) open_port_event_p);
			}
		default:
			{

				/* Withdraw the instance gathering the port information */
				atp_delete_port (new_port_p);
				
				/* Decrease number of port currently in use */
				initiator_p->nb_open_port--;
				target_p->nb_open_port--;
				atp_error_switch (ATP_ERROR_FAILED_TO_OPEN_A_PORT,
								  ATP_MEMORY_ERROR,
								  NULL);
				return (RV_MEMORY_ERR);
			}
	}
	return (atp_send_message (target_p->return_path, \
							  (T_ATP_MESSAGE *) open_port_event_p));
}


/******************************************************************************
* Function name: atp_open_port_rsp
*
* Description : Response from the target to a open port request
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*					RV_NOT_SUPPORTED : port already open 
*					RV_INTERNAL_ERR : Error generated because bad initialisation of MB
*
*					atp_error can be called if MB is RED 
* 
* History			: 0.1 (1-Marsh-2000) - Created
*					: 0.9  (5-May-2000) - Reviewed
******************************************************************************/

T_ATP_RET atp_open_port_rsp(T_ATP_SW_ENTITY_ID initiator_id, T_ATP_PORT_NB initiator_port_nb,
							T_ATP_SW_ENTITY_ID target_id, T_ATP_PORT_NB target_port_nb, 
							T_ATP_PORT_INFO port_info, T_ATP_NO_COPY_INFO no_copy_info, 
							T_ATP_CUSTOM_INFO * custom_info_p, T_ATP_OPEN_PORT_RESULT result)
							
{
	T_ATP_SW_NB             sw_nb                  = 0;
	T_ATP_PORT_STRUCT       *port_p                = NULL;
	T_ATP_OPEN_PORT_CFM     *open_port_cfm_event_p = NULL;
	T_ATP_SW_ENTITY_STRUCT  *initiator_p           = NULL;
	T_ATP_SW_ENTITY_STRUCT  *target_p              = NULL;
	
	/* Check for invalid parameters, get the pointers on the structures that */
	/* gather information about the initiator and remote SW entities and get */
	/* the port associated with initiator SW entity */
	if ((initiator_id > ATP_MAX_NB_SW_ENTITY) || \
		(target_id > ATP_MAX_NB_SW_ENTITY) || \
		((initiator_p = atp_sw_entity_table_p[initiator_id]) == NULL) || \
		((target_p = atp_sw_entity_table_p[target_id]) == NULL) || \
		(atp_get_port (initiator_id, \
					   initiator_port_nb, \
					   &port_p, \
					   &sw_nb) != RV_OK))
	{
		atp_error_switch (ATP_ERROR_FAILED_TO_ACCEPT_A_PORT,
						  ATP_PARAM_ERROR,
						  NULL);
		return (RV_INVALID_PARAMETER);
	}

	/* Check whether the port is waiting for an ATP_OPEN_PORT_CFM event */
	if (port_p->port_state != ATP_OPEN_PENDING)
	{
		atp_error_switch (ATP_ERROR_FAILED_TO_ACCEPT_A_PORT,
						  ATP_ISSUED_IN_A_WRONG_STATE_ERROR,
						  NULL);
		return (RV_NOT_SUPPORTED);
	}

	/* Fill in the ATP_OPEN_PORT_CFM event to be sent back to the initiator */
    if (rvf_get_buf (atp_mb_prim, \
					 sizeof (T_ATP_OPEN_PORT_CFM), \
					 (T_RVF_BUFFER **) &open_port_cfm_event_p) == RVF_RED)
	{

		/* Withdraw the instance gathering the port information */
		atp_delete_port (port_p);
		atp_error (ATP_ERROR_MB_PRIM_RED);
		return (RV_MEMORY_ERR);
	}
	(open_port_cfm_event_p->rv_hdr).msg_id	 = ATP_OPEN_PORT_CFM;
	open_port_cfm_event_p->initiator_port_nb = initiator_port_nb;
	open_port_cfm_event_p->custom_info_p	 = custom_info_p;

	/* Send a negative ATP_OPEN_PORT_CFM event right now whether the target */
	/* denies opening the port */
	if ((open_port_cfm_event_p->result = result) != OPEN_PORT_OK)
	{

		/* Withdraw the instance gathering the port information */
		atp_delete_port (port_p);
				
		/* Decrease number of port currently in use */
		initiator_p->nb_open_port--;
		target_p->nb_open_port--;

		/* Send a negative ATP_OPEN_PORT_CFM event to the initiator */
		atp_send_message (initiator_p->return_path,
						  (T_ATP_MESSAGE *) open_port_cfm_event_p);
		return (RV_OK);
	}
	
	/* By default, set the port in 'Command Mode'. Hence, the port is ready */
	/* to exchange AT commands */
	port_p->port_state = ATP_CMD_MODE;

	/* Update information about the target */
	(port_p->port_info[1]).port_nb     = target_port_nb;
	(port_p->port_info[1]).ring_type   = port_info.ring_type;
	(port_p->port_info[1]).signal_mask = (T_ATP_SIGNAL_MASK) (port_info.signal_mask & ATP_ALL_THE_SIGNAL_UNMASK);

	/* One of the SW entity is a transport layer that does not handle AT commands */
	if ((port_p->port_config == NOT_DEFINED_CONFIG) && \
		((target_p->mode).cmd_support_mode == CMD_SUPPORT_OFF))
	{
		
		/* Check for an invalid configuration */
		if (port_info.port_config > NOT_DEFINED_CONFIG)
		{
			atp_error_switch (ATP_ERROR_FAILED_TO_OPEN_A_PORT,
							  ATP_PARAM_ERROR,
							  NULL);

			/* Clean up */
			rvf_free_buf ((T_RVF_BUFFER *) open_port_cfm_event_p);
			return (RV_INVALID_PARAMETER);
		}

		/* Update the port configuration */
		port_p->port_config = port_info.port_config;
	}

	/* Get settings related the COPY_OFF mode */
	if ((target_p->mode).cp_mode == COPY_OFF)
	{

		/* Get the number of bytes left free for the RX header and trailer */
		if (no_copy_info.rx_head_mode == RX_HEADER_ON)
		{
			((port_p->port_info[1]).no_copy_info).rx_head_size  = no_copy_info.rx_head_size;
			((port_p->port_info[1]).no_copy_info).rx_trail_size = no_copy_info.rx_trail_size;
		}

		/* Get the number of bytes left free for the TX header and trailer */
		if (no_copy_info.tx_head_mode == TX_HEADER_ON)
		{
			((port_p->port_info[1]).no_copy_info).tx_head_size  = no_copy_info.tx_head_size;
			((port_p->port_info[1]).no_copy_info).tx_trail_size = no_copy_info.tx_trail_size;
		}

		/* Get memory bank information */
		if (no_copy_info.rx_mb != RVF_INVALID_MB_ID)
		{
			(port_p->port_info[1]).rx_mb = no_copy_info.rx_mb;
		}
		if (no_copy_info.tx_mb != RVF_INVALID_MB_ID)
		{
			(port_p->port_info[1]).tx_mb = no_copy_info.tx_mb;
		}

		/* Get the packet mode (i.e. SEGMENTED_PACKET) */
		((port_p->port_info[1]).no_copy_info).packet_mode = no_copy_info.packet_mode;
	}

	/* If only one of the SW entities has provided valid memory banks, */
	/* copy those information on both sides. Of course TX -> RX and RX -> TX */
	if ((port_p->port_info[0]).rx_mb == RVF_INVALID_MB_ID)
	{
		(port_p->port_info[0]).rx_mb = (port_p->port_info[1]).tx_mb;
	}
	if ((port_p->port_info[0]).tx_mb == RVF_INVALID_MB_ID)
	{
		(port_p->port_info[0]).tx_mb = (port_p->port_info[1]).rx_mb;
	}

#ifdef ATP_DEBUG_MSG_ENABLED
			(void) sprintf (gbl_debug_message,
							"ATP: Init (RX: 0x%.4X TX: 0x%.4X), Target (RX: 0x%.4X TX: 0x%.4X) ",
							port_p->port_info[0].rx_mb,
							port_p->port_info[0].tx_mb,
							port_p->port_info[1].rx_mb,
							port_p->port_info[1].tx_mb);
			rvf_send_trace (gbl_debug_message,
							(UINT8) strlen (gbl_debug_message),
							NULL_PARAM,
							RV_TRACE_LEVEL_DEBUG_MEDIUM,
							ATP_USE_ID);
#endif

	/* At the end, no memory banks should be invalid */
	if ((port_p->port_info[0].tx_mb == RVF_INVALID_MB_ID) || \
		(port_p->port_info[0].rx_mb == RVF_INVALID_MB_ID) || \
		(port_p->port_info[1].tx_mb == RVF_INVALID_MB_ID) || \
		(port_p->port_info[1].rx_mb == RVF_INVALID_MB_ID))
	{
		ATP_SEND_TRACE ("ATP: Some memory banks not specified ",
						RV_TRACE_LEVEL_ERROR);
		atp_error_switch (ATP_ERROR_FAILED_TO_ACCEPT_A_PORT,
						  ATP_PARAM_ERROR,
						  NULL);

		/* Clean up */
		rvf_free_buf ((T_RVF_BUFFER *) open_port_cfm_event_p);
		return (RV_INTERNAL_ERR);
	}

	/* If needed, allocate a buffer for DCE or DTE information */
	if (((initiator_p->mode).cmd_support_mode == CMD_SUPPORT_OFF) || \
		((target_p->mode).cmd_support_mode == CMD_SUPPORT_OFF))
	{
		switch (port_p->port_config)
		{
			case DCE_CONFIG:
			case DTE_CONFIG:
				{
					T_ATP_DCE_INFO  *dce_info_p = NULL;
					
					ATP_SEND_TRACE ("ATP: A port has been open with DCE/DTE mode activated",
									RV_TRACE_LEVEL_DEBUG_LOW);

					/* Allocate a buffer for DCE or DTE information */
					if (rvf_get_buf (atp_mb_prim, \
									 sizeof (T_ATP_DCE_INFO), \
									 (T_RVF_BUFFER **) &dce_info_p) == RVF_RED)
					{
						atp_error_switch (ATP_ERROR_FAILED_TO_ACCEPT_A_PORT,
										  ATP_MEMORY_ERROR,
										  NULL);

						/* Clean up */
						rvf_free_buf ((T_RVF_BUFFER *) open_port_cfm_event_p);
						return (RV_MEMORY_ERR);
					}

					/* Initialize DCE or DTE information */
					atp_cmd_init_dce_info (dce_info_p);
					port_p->dce_info_p = dce_info_p;
					break;
				}
			case DATA_CONFIG:
				{

					/* The port is plugged on a transport layer */
					port_p->port_state = ATP_DATA_MODE;
					break;
				}
			default:
				{
					ATP_SEND_TRACE ("ATP: Failed to open a port (port configuration not specified)",
									RV_TRACE_LEVEL_ERROR);
					atp_error_switch (ATP_ERROR_FAILED_TO_ACCEPT_A_PORT,
									  ATP_PARAM_ERROR,
									  NULL);

					/* Clean up */
					rvf_free_buf ((T_RVF_BUFFER *) open_port_cfm_event_p);
					return (RV_INTERNAL_ERR);
				}
		}
	}

	/* At last, send an ATP_OPEN_PORT_CFM event to the initiator */
	return (atp_send_message (initiator_p->return_path, \
							  (T_ATP_MESSAGE *) open_port_cfm_event_p));
}




/******************************************************************************
* Function name: atp_close_port
*
* Description : Close a port
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*
*					atp_error can be called if MB is RED 
* 
* History			: 0.1 (1-Marsh-2000) - Created
*					: 0.9  (5-May-2000) - Reviewed
******************************************************************************/
T_ATP_RET atp_close_port(T_ATP_SW_ENTITY_ID closer_sw_id, T_ATP_PORT_NB closer_port_nb)
{
	T_ATP_SW_ENTITY_STRUCT * closer_sw_id_p, * other_sw_id_p;
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB closer_sw_nb,other_sw_nb;
	T_ATP_PORT_CLOSED * port_closed_event_p;
	
	/* Get the pointer on the port structure */
	if(atp_get_port(closer_sw_id,closer_port_nb,&port_p,&closer_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_CLOSE_A_PORT,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	
	other_sw_nb= (T_ATP_SW_NB) (! closer_sw_nb);
	
	
	
	/* Send an event to the other sw entity indicating the port has been closed */
    if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_PORT_CLOSED),(void **) &port_closed_event_p)==RVF_RED)
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_CLOSE_A_PORT,ATP_MEMORY_ERROR,NULL);
		return RV_MEMORY_ERR;
	}
	
	port_closed_event_p->rv_hdr.msg_id	= ATP_PORT_CLOSED;
	port_closed_event_p->port_nb		= port_p->port_info[other_sw_nb].port_nb;
	port_closed_event_p->closer_port_nb = closer_port_nb;
	port_closed_event_p->closer_sw_id	= closer_sw_id;

	/* Get pointer on the other sw entity data structure */
	other_sw_id_p=atp_sw_entity_table_p[port_p->port_info[other_sw_nb].sw_id];
	closer_sw_id_p = atp_sw_entity_table_p[closer_sw_id];

	/* Send the event and update open port nb */
	
	atp_send_message(other_sw_id_p->return_path,(T_ATP_MESSAGE *) port_closed_event_p);
	
	/* Decrease number of port for each SW entity */
	other_sw_id_p->nb_open_port--;
	closer_sw_id_p->nb_open_port--;

	atp_delete_port(port_p);
	
	
	return RV_OK;
}





/******************************************************************************
* Function name: atp_send_cmd
*
* Description : Send a command
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*					RV_NOT_SUPPORTED : command needed to be translated and was unknow by ATP
*
*					atp_error can be called if MB is RED 
*
* History		: 0.1 (1-Marsh-2000) - Created
*				: 0.9  (5-May-2000) - Reviewed
*
* Note :		Sender SW Entity cannot in DCE_ON
*				
******************************************************************************/
T_ATP_RET atp_send_cmd(T_ATP_SW_ENTITY_ID sender_sw_id, T_ATP_PORT_NB sender_port_nb,
					   T_ATP_CMD_TYPE cmd_type,T_ATP_CMD_NB cmd_nb, T_ATP_CMD  * cmd_info_p)
{
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB sender_sw_nb, other_sw_nb;
	T_ATP_CMD_RDY * cmd_ready_event_p;
	T_ATP_TXT_CMD_RDY * txt_cmd_ready_event_p;
	T_ATP_SW_ENTITY_STRUCT * other_sw_entity_p;
	T_ATP_TXT_CMD  text_p;
	UINT16 length;
	T_ATP_BUFFER atp_buffer_p;
	T_ATP_NO_COPY_DATA_RDY * no_copy_data_ready_p;
	T_ATP_RET return_status;
	
	/* Get the pointer on the port structure */
	if(atp_get_port(sender_sw_id,sender_port_nb,&port_p,&sender_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	other_sw_nb=(T_ATP_SW_NB) (! sender_sw_nb);
	
	if (port_p->port_state == ATP_OPEN_PENDING)
	{
		/* Port is not completely open */
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_ISSUED_IN_A_WRONG_STATE_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	/* get pointer on the other SW entity data structure in order to get info */
	other_sw_entity_p=atp_sw_entity_table_p[port_p->port_info[other_sw_nb].sw_id];
		
	/* Check if the port is available */
	if ( (port_p->cmd_info.state != READY_FOR_NEW_CMD) &&
		(cmd_type == AT_CMD))
	{	// A new command cannot been sent if a result code has not been previously received */
		rvf_send_trace("ATP : Refused to send a new command on a port which was waiting for a result ",77,
			NULL_PARAM,RV_TRACE_LEVEL_WARNING, ATP_USE_ID); 		
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_WAITING_FOR_RESULT,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	if (cmd_type == AT_CMD)
	{
		port_p->cmd_info.state = WAITING_FOR_RESULT; // Prevent any new command entry
	}
	
	
	
	if (other_sw_entity_p->mode.cmd_support_mode == CMD_SUPPORT_OFF)
	{
		/* Other SW entity does not support to get command directly */
		if ( (port_p->port_config == DATA_CONFIG) ||
			 (port_p->port_state == ATP_DATA_MODE) )
		{
			ATP_SEND_TRACE ("ATP: A command cannot be sent on a DATA_ONLY configuration port",RV_TRACE_LEVEL_WARNING);
			atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_WAITING_FOR_RESULT,NULL);
			return RV_NOT_SUPPORTED;
		}
		
		/* DCE or DTE emulation is ON */
		/* Must translate the command and put it into a buffer of data */
		
		/* Check if the answer is a result code from a ON_GOING interpretation of a raw data buffer 
		containing several commands */
		if ((cmd_type == RESULT_CODE) && (port_p->cmd_info.status == ON_GOING) && (cmd_nb == ATP_OK_NB))
		{	
			// Then interpret data. Either sends result to peer device or resend a command to appli
			// START SEMAPHORE 
			port_p->cmd_info.state=READY_FOR_NEW_CMD;
			return_status=atp_interpret_data(port_p,other_sw_nb,sender_sw_nb);
			// STOP SEMAPHORE
			// other = transport layer , sender = appli
			
			if (return_status == RV_MEMORY_ERR)
			{
				atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_MEMORY_ERROR,NULL);
				return RV_MEMORY_ERR;
			}
			
			return RV_OK;
		}		   
		
		/* Otherwise can be : result code was not OK -> so re-init cmd_info and send the result code to peer
		normal command to send
		final result to send -> so re-init cmd_info and send the result code to peer
		
		/* Obtain buffer with command translated into data buffer ready for TX*/
		if(atp_create_data_buffer_from_cmd(INTERPRETED_MODE,
			port_p->port_info[other_sw_nb].no_copy_info.rx_head_size,
			port_p->port_info[other_sw_nb].no_copy_info.rx_trail_size,
			(T_ATP_DCE_INFO *) port_p->dce_info_p,
			port_p->port_info[other_sw_nb].rx_mb,
			cmd_type,
			cmd_nb,NULL,cmd_info_p,
			&atp_buffer_p,&length)!=RV_OK)
		{
			atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_CANNOT_TRANSLATE_CMD,NULL);
			return RV_NOT_SUPPORTED;
		}
		
		/* Send it to the other SW entity via an ATP_NO_COPY_DATA_RDY */
		/* Note: it is assumed that only COPY OFF instance can be DCE ON */
		if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_NO_COPY_DATA_RDY),(void **) &no_copy_data_ready_p)==RVF_RED)
		{
			atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_MEMORY_ERROR,NULL);
			return RV_MEMORY_ERR;
		}		
		
		no_copy_data_ready_p->rv_hdr.msg_id	= ATP_NO_COPY_DATA_RDY;
		no_copy_data_ready_p->port_nb		= port_p->port_info[other_sw_nb].port_nb;
		no_copy_data_ready_p->buffer_size	= length-port_p->port_info[other_sw_nb].no_copy_info.rx_head_size
						-port_p->port_info[other_sw_nb].no_copy_info.rx_trail_size;
		no_copy_data_ready_p->atp_buffer_p	= atp_buffer_p;
		
		/* Re-accept to deal with any new command */
		if (cmd_type == RESULT_CODE)
		{
			atp_init_cmd_info_struct(port_p); /* Re-initilalise all the cmd_info structure */
		}
		else 
		{
			if(cmd_type == AT_CMD) 
			{
				port_p->cmd_info.state=WAITING_FOR_RESULT;
			}
		}
		atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *) no_copy_data_ready_p);
			
		return RV_OK;	
		
	} /* End of the DCE case */
	
	
	
	
	
	if(other_sw_entity_p->mode.cmd_mode==TXT_MODE)
	{
		/* Other SW entity wants command in text format */
		if (atp_translate_cmd_to_txt(cmd_type,cmd_nb,cmd_info_p,
			port_p->port_info[other_sw_nb].rx_mb,&text_p,&length)==RV_OK)
		{
			/* The command has been properly translated */
			/* Send an event to the other sw entity with the text buffer*/	
			if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_TXT_CMD_RDY),(void **) &txt_cmd_ready_event_p)==RVF_RED)
			{
				atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_MEMORY_ERROR,NULL);
				return RV_MEMORY_ERR;
			}
			
			txt_cmd_ready_event_p->rv_hdr.msg_id	= ATP_TXT_CMD_RDY;	
			txt_cmd_ready_event_p->cmd_type			= cmd_type;
			txt_cmd_ready_event_p->txt_cmd_p		= text_p;
			txt_cmd_ready_event_p->port_nb			= port_p->port_info[other_sw_nb].port_nb;
			
			/* Re-accept to deal with any new command */
			if (cmd_type == RESULT_CODE) { port_p->cmd_info.state=READY_FOR_NEW_CMD;}
			
			/* Send the event */
			atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *) txt_cmd_ready_event_p);
			return RV_OK;
		}
		
		/* This command cannot be translated into text */
		/* Send a command event without text translation */
		rvf_send_trace("ATP : CMD coult not be translated into text format ",51,NULL_PARAM,
			RV_TRACE_LEVEL_WARNING, ATP_USE_ID); 
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_CANNOT_TRANSLATE_CMD,NULL);
		return RV_NOT_SUPPORTED;
		
	}
	
	/* Otherwise, means that target SWE is also in CMD format */
	/* Other SW entity will have command in same format (command format) */
	/* Send an event to the other sw entity forwarding the command */	
	if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_CMD_RDY),(void **) &cmd_ready_event_p)==RVF_RED)
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_MEMORY_ERROR,NULL);
		return RV_MEMORY_ERR;
	}
	
	cmd_ready_event_p->rv_hdr.msg_id	= ATP_CMD_RDY;	
	cmd_ready_event_p->cmd_type			= cmd_type;
	cmd_ready_event_p->cmd_nb			= cmd_nb;
	cmd_ready_event_p->cmd_info_p		= cmd_info_p;
	cmd_ready_event_p->port_nb			= port_p->port_info[other_sw_nb].port_nb;
	
	/* Re-accept to deal with any new command */
	if (cmd_type == RESULT_CODE) { port_p->cmd_info.state=READY_FOR_NEW_CMD;}
	
	/* Send the event */
	atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *) cmd_ready_event_p);
	return RV_OK;
}








/******************************************************************************
* Function name: atp_send_txt_cmd
*
* Description : Send a command in text format
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*
*					atp_error can be called if MB is RED 
*					If the cmd needed to be translated and was unknown by ATP, the cmd is sent
*					in text format
*
* History		: 0.1 (1-Marsh-2000) - Created
*				: 0.9  (5-May-2000) - Reviewed
******************************************************************************/
T_ATP_RET atp_send_txt_cmd(T_ATP_SW_ENTITY_ID sender_sw_id, T_ATP_PORT_NB sender_port_nb,
						   T_ATP_CMD_TYPE cmd_type, T_ATP_TXT_CMD txt_p)
{
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB sender_sw_nb, other_sw_nb;
	T_ATP_CMD_RDY * cmd_ready_event_p;
	T_ATP_TXT_CMD_RDY * txt_cmd_ready_event_p;
	T_ATP_SW_ENTITY_STRUCT * other_sw_entity_p;
	T_ATP_NO_COPY_DATA_RDY * no_copy_data_ready_p;
	T_ATP_CMD * cmd_info_p;
	T_ATP_CMD_TYPE found_cmd_type;
	T_ATP_CMD_NB found_cmd_nb;
	T_ATP_BUFFER atp_buffer_p;
	T_ATP_RET return_status;
	
	UINT16 length;
	void * dummy_p = NULL;
	
	
	/* Get the pointer on the port structure */
	if(atp_get_port(sender_sw_id,sender_port_nb,&port_p,&sender_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	other_sw_nb=(T_ATP_SW_NB) (! sender_sw_nb);
	
	/* get pointer on the other SW entity data structure in order to get info */
	other_sw_entity_p=atp_sw_entity_table_p[port_p->port_info[other_sw_nb].sw_id];
	
	if (port_p->port_state == ATP_OPEN_PENDING)
	{
		/* Port is not completely open */
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_ISSUED_IN_A_WRONG_STATE_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	if ( (other_sw_entity_p->mode.cmd_support_mode==CMD_SUPPORT_OFF) &&
		(port_p->port_state != ATP_CMD_MODE))
	{
		ATP_SEND_TRACE ("ATP : Refused to send a command to a SWE in data mode ",
			RV_TRACE_LEVEL_WARNING); 		
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_WAITING_FOR_RESULT,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	
	/* Check if the port is available */
	if ( (port_p->cmd_info.state != READY_FOR_NEW_CMD) &&
		(cmd_type == AT_CMD))
	{	// A new command cannot been sent if a result code has not been previously received */
		rvf_send_trace("ATP : Refused to send a new command on a port which was waiting for a result ",77,
			NULL_PARAM,RV_TRACE_LEVEL_WARNING, ATP_USE_ID); 
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_WAITING_FOR_RESULT,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	if (cmd_type == AT_CMD)
	{
		port_p->cmd_info.state = WAITING_FOR_RESULT; // Prevent any new command entry
	}
	
	
	
	
	if (other_sw_entity_p->mode.cmd_support_mode==CMD_SUPPORT_OFF)
	{
		/* Other SW entity does not support to get command directly */
		if (port_p->port_config == DATA_CONFIG)
		{
			ATP_SEND_TRACE ("ATP: A command cannot be sent on a DATA_ONLY configuration port",RV_TRACE_LEVEL_WARNING);
			atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_WAITING_FOR_RESULT,NULL);
			return RV_NOT_SUPPORTED;
		}
		
		/* DCE or DTE emulation is ON */
		/* Must translate the command and put it into a buffer of data */
		found_cmd_type = cmd_type;
		found_cmd_nb = 0xFF;
		
		if ((found_cmd_type == UNKNOWN) || (found_cmd_type == RESULT_CODE))
		{
			// Check if the answer is a RESULT_CODE with OK or ERROR
			atp_translate_txt_to_cmd(txt_p,cmd_type,&found_cmd_type,&found_cmd_nb,
				port_p->port_info[other_sw_nb].rx_mb,
				(T_ATP_CMD **) &dummy_p); // => found_cmd_type and found_cmd_nb may be updated now
		}
		
		/* Check if the answer is a result code from a ON_GOING interpretation of a raw data buffer 
		containing several commands */
		if ((found_cmd_type == RESULT_CODE) && (port_p->cmd_info.status == ON_GOING) && (found_cmd_nb == ATP_OK_NB))
		{	
			rvf_free_buf(txt_p);
			// Then interpret data. Either sends result to peer device or resend a command to appli
			// START SEMAPHORE 
			port_p->cmd_info.state=READY_FOR_NEW_CMD;
			return_status=atp_interpret_data(port_p,other_sw_nb,sender_sw_nb);
			// STOP SEMAPHORE
			// other = transport layer , sender = appli
			
			if (return_status == RV_MEMORY_ERR)
			{
				atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_MEMORY_ERROR,NULL);
				return RV_MEMORY_ERR;
			}
			
			return RV_OK;
		}		   
		
		
		/* Otherwise can be : result code was not OK -> so re-init cmd_info and send the result code to peer
		normal command to send
		final result to send -> so re-init cmd_info and send the result code to peer */
		
		
		/* Obtain buffer with command translated into data buffer ready for TX*/
		if(atp_create_data_buffer_from_cmd(TXT_MODE,
			port_p->port_info[other_sw_nb].no_copy_info.rx_head_size,
			port_p->port_info[other_sw_nb].no_copy_info.rx_trail_size,
			port_p->dce_info_p,
			port_p->port_info[other_sw_nb].rx_mb,
			found_cmd_type,0,txt_p,NULL,
			&atp_buffer_p,&length)!=RV_OK)
		{
			rvf_free_buf(txt_p);
			atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_CANNOT_TRANSLATE_CMD,NULL);
			return RV_NOT_SUPPORTED;
		}
		
		/* Send it to the other SW entity via an ATP_NO_COPY_DATA_RDY */
		/* Note: it is assumed that only COPY OFF instance can be DCE ON */
		if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_NO_COPY_DATA_RDY),(void **) &no_copy_data_ready_p)==RVF_RED)
		{
			atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_MEMORY_ERROR,NULL);
			return RV_MEMORY_ERR;
		}		
		
		no_copy_data_ready_p->rv_hdr.msg_id	= ATP_NO_COPY_DATA_RDY;
		no_copy_data_ready_p->port_nb		= port_p->port_info[other_sw_nb].port_nb;
		no_copy_data_ready_p->buffer_size	= length-port_p->port_info[other_sw_nb].no_copy_info.rx_head_size
			-port_p->port_info[other_sw_nb].no_copy_info.rx_trail_size;
		no_copy_data_ready_p->atp_buffer_p	= atp_buffer_p;
		
		
		/* Re-accept to deal with any new command */
		if (found_cmd_type == RESULT_CODE)
		{
			atp_init_cmd_info_struct(port_p); /* Re-initilalise all the cmd_info structure */
		}
		else 
		{
			if(found_cmd_type == AT_CMD) 
			{
				port_p->cmd_info.state=WAITING_FOR_RESULT;
			}
		}		atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *) no_copy_data_ready_p);
		
		return RV_OK;				
	}
	
	
	if(other_sw_entity_p->mode.cmd_mode==INTERPRETED_MODE)
	{	
		/* Other SW entity will have command in interpreted format  */
		if (atp_translate_txt_to_cmd((T_ATP_TXT_CMD) txt_p,cmd_type,&found_cmd_type,
			&found_cmd_nb,port_p->port_info[other_sw_nb].rx_mb,
			&cmd_info_p)==RV_OK)
		{
			/* The command has been properly translated into command format*/
			/* Release text buffer */
			rvf_free_buf(txt_p);
			
			/* Send an event to the other sw entity with the command buffer*/	
			if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_CMD_RDY),(void **) &cmd_ready_event_p)==RVF_RED)
			{
				atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_MEMORY_ERROR,NULL);
				return RV_MEMORY_ERR;
			}
			
			cmd_ready_event_p->rv_hdr.msg_id=ATP_CMD_RDY;	
			cmd_ready_event_p->cmd_type=found_cmd_type;
			cmd_ready_event_p->cmd_nb=found_cmd_nb;
			cmd_ready_event_p->cmd_info_p=cmd_info_p;
			cmd_ready_event_p->port_nb=port_p->port_info[other_sw_nb].port_nb;
			
			/* Re-accept to deal with any new command */
			if (found_cmd_type == RESULT_CODE) { port_p->cmd_info.state=READY_FOR_NEW_CMD;}

			/* Send the event */
			atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *) cmd_ready_event_p);			
			return RV_OK;
		}
		/* Otherwise, the text has not been translated properly
		Forward to the SW entity in text format */
	}
	
	
	
	/* Send Command in text format */
	/* Send an event to the other sw entity with the text buffer*/	
	if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_TXT_CMD_RDY),(void **) &txt_cmd_ready_event_p)==RVF_RED)
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_MEMORY_ERROR,NULL);
		return RV_MEMORY_ERR;
	}
	
	txt_cmd_ready_event_p->rv_hdr.msg_id=ATP_TXT_CMD_RDY;	
	txt_cmd_ready_event_p->cmd_type=cmd_type;
	txt_cmd_ready_event_p->txt_cmd_p=txt_p;
	txt_cmd_ready_event_p->port_nb=port_p->port_info[other_sw_nb].port_nb;
	
	/* Re-accept to deal with any new command */
	if ( (cmd_type == RESULT_CODE) ||
	     (cmd_type == UNKNOWN)) // This case is to deal with the case of TXT SWE 1 sending a result code with cmd_type = UNKNOW
								// to another TXT SWE  
	{ port_p->cmd_info.state=READY_FOR_NEW_CMD;}

	/* Send the event */
	atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *) txt_cmd_ready_event_p);
			
	return RV_OK;
}









/******************************************************************************
* Function name: atp_send_data
*
* Description : Send data on a port
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*
*					atperror can be called if MB is RED 
*
* History		: 0.1 (1-Marsh-2000) - Created
*				: 0.9  (5-May-2000) - Reviewed
******************************************************************************/
T_ATP_RET atp_send_data(T_ATP_SW_ENTITY_ID sender_sw_id, T_ATP_PORT_NB sender_port_nb,
						void * data_buffer_p, UINT32 buffer_size, UINT32 *nb_bytes_left_p)
{
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB sender_sw_nb;
	T_ATP_SW_ENTITY_STRUCT * other_sw_entity_p;
	UINT32 real_buffer_size;
	T_ATP_NO_COPY_DATA_RDY *no_copy_data_ready_p;
	T_ATP_DATA_RDY *data_ready_p;
	T_ATP_RX_PACKET *rx_packet_p;
	UINT32 i,offset;
	T_ATP_BUFFER rvf_buffer_p;
	T_ATP_PORT_END_STRUCT * other_port_info_p;

#ifdef ATP_DEBUG_MSG_ENABLED
	(void) sprintf (gbl_debug_message,
					"ATP: send_data invoked (Size: %d, Port: %d) ",
					buffer_size,
					sender_port_nb);
	rvf_send_trace (gbl_debug_message,
					(UINT8) strlen (gbl_debug_message),
					NULL_PARAM,
					RV_TRACE_LEVEL_DEBUG_MEDIUM,
					ATP_USE_ID);
#endif

#ifdef ATP_HEX_DUMP_ENABLED
	atp_hexdump_buf (data_buffer_p,
					 buffer_size);
#endif

	/* Bytes left to be sent: sender should wait and retransmit later on if needed */
	*nb_bytes_left_p=buffer_size;

	/* Get the pointer on the port structure */
	if(atp_get_port(sender_sw_id,sender_port_nb,&port_p,&sender_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	
	if (port_p->port_state == ATP_OPEN_PENDING)
	{
		/* Port is not completely open */
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_ISSUED_IN_A_WRONG_STATE_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	/* Get other port */
	if ((port_p->redirect_mode==ATP_REDIRECT_ON) && 
		(port_p->port_state==ATP_DATA_MODE)) /* redirection is activated */
	{
		other_port_info_p= & (port_p->redirect_port_p->port_info[port_p->redirect_port_end_nb]);
	}
	else
	{
		other_port_info_p= & (port_p->port_info[(! sender_sw_nb)]);
	}
	
	
	/* get pointer on the other SW entity data structure in order to get info */
	other_sw_entity_p=atp_sw_entity_table_p[(* other_port_info_p).sw_id];
	
	
	/* Get a buffer to send or store the data */
	/* real_buffer_size is the size of the buffer to transmit */
	if(other_sw_entity_p->mode.cp_mode==COPY_OFF)
	{	/* In this case, need to eventually add header and trailer sizes*/
		real_buffer_size=buffer_size+other_port_info_p->no_copy_info.rx_head_size
			+ other_port_info_p->no_copy_info.rx_trail_size;
	}
	else
	{
		real_buffer_size=buffer_size;
	}

	if (rvf_get_buf(other_port_info_p->rx_mb,real_buffer_size,(void **) &rvf_buffer_p)==RVF_RED)
	{ /* No memory available to store the data */

#ifdef ATP_DEBUG_MSG_ENABLED
		(void) sprintf (gbl_debug_message,
						"ATP: send_data. Insufficient memory (Bytes left: %d, Bank: %d) ",
						*nb_bytes_left_p,
						other_port_info_p->rx_mb);
		rvf_send_trace (gbl_debug_message,
						(UINT8) strlen (gbl_debug_message),
						NULL_PARAM,
						RV_TRACE_LEVEL_WARNING,
						ATP_USE_ID);
#endif
		
		/* Sends a signal to stop the TX FLOW CONTROL of the sender 
		ie the receiver set its RX_FLOW_CTRL to OFF  */
		atp_set_signal(other_port_info_p->sw_id,other_port_info_p->port_nb,
			ATP_RX_FLOW_OFF,ATP_RX_FLOW_UNMASK); 
		return RV_NOT_SUPPORTED;
	}
	
	
	/* Copy data into RVF buffer */
	offset=other_port_info_p->no_copy_info.rx_head_size;
	for(i=0;i<buffer_size;i++)
	{
		rvf_buffer_p[i+offset]=((UINT8 *) data_buffer_p)[i];
	}
	
	
	switch (other_sw_entity_p->mode.cp_mode)
	{
		case COPY_OFF:
		{	
			/* Send a ATP_NO_COPY_DATA_RDY event */
			if (rvf_get_buf(other_port_info_p->rx_mb,sizeof(T_ATP_NO_COPY_DATA_RDY),(void **) &no_copy_data_ready_p)==RVF_RED)
			{
#ifdef ATP_DEBUG_MSG_ENABLED
				(void) sprintf (gbl_debug_message,
								"ATP: send_data. Insufficient memory (Bytes left: %d, Bank: %d) ",
								*nb_bytes_left_p,
								other_port_info_p->rx_mb);
				rvf_send_trace (gbl_debug_message,
								(UINT8) strlen (gbl_debug_message),
								NULL_PARAM,
								RV_TRACE_LEVEL_WARNING,
								ATP_USE_ID);
#endif
			
				/* Sends a signal to stop the TX FLOW CONTROL of the sender 
				ie the receiver set its RX_FLOW_CTRL to OFF  */
				atp_set_signal(other_port_info_p->sw_id,other_port_info_p->port_nb,
					ATP_RX_FLOW_OFF,ATP_RX_FLOW_UNMASK); 

				rvf_free_buf (rvf_buffer_p);
				return RV_NOT_SUPPORTED;
			}		
			
			no_copy_data_ready_p->rv_hdr.msg_id=ATP_NO_COPY_DATA_RDY;
			no_copy_data_ready_p->port_nb=other_port_info_p->port_nb;
			no_copy_data_ready_p->buffer_size=buffer_size; /* Indicate only length of payload ! */
			no_copy_data_ready_p->atp_buffer_p=rvf_buffer_p;

#ifdef ATP_DEBUG_MSG_ENABLED
			(void) sprintf (gbl_debug_message,
							"ATP: NO_COPY_DATA_RDY sent (Port: %d, Size: %d) ",
							no_copy_data_ready_p->port_nb,
							no_copy_data_ready_p->buffer_size);
			rvf_send_trace (gbl_debug_message,
							(UINT8) strlen (gbl_debug_message),
							NULL_PARAM,
							RV_TRACE_LEVEL_DEBUG_MEDIUM,
							ATP_USE_ID);
#endif

			/* Send the event */
			atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *) no_copy_data_ready_p);
			*nb_bytes_left_p=0;
			return RV_OK;
		}
		case COPY_ON:
		{	

		   /* Queue the packet and send an ATP_DATA_RDY event */		
		   /* Get enqueue header */
		   if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_RX_PACKET),(void **) &rx_packet_p)==RVF_RED)
		   {
			   rvf_free_buf (rvf_buffer_p);
			   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=real_buffer_size-1;
		   rx_packet_p->atp_buffer_p=rvf_buffer_p;
		   rx_packet_p->next_byte_to_read=rx_packet_p->first_byte;

		   /* Get an ATP_DATA_RDY event */
		   if (rvf_get_buf(other_port_info_p->rx_mb,sizeof(T_ATP_DATA_RDY),(void **) &data_ready_p)==RVF_RED)
		   {
#ifdef ATP_DEBUG_MSG_ENABLED
				(void) sprintf (gbl_debug_message,
								"ATP: send_data. Insufficient memory (Bytes left: %d, Bank: %d) ",
								*nb_bytes_left_p,
								other_port_info_p->rx_mb);
				rvf_send_trace (gbl_debug_message,
								(UINT8) strlen (gbl_debug_message),
								NULL_PARAM,
								RV_TRACE_LEVEL_WARNING,
								ATP_USE_ID);
#endif
			
				/* Sends a signal to stop the TX FLOW CONTROL of the sender 
				ie the receiver set its RX_FLOW_CTRL to OFF  */
				atp_set_signal(other_port_info_p->sw_id,other_port_info_p->port_nb,
					ATP_RX_FLOW_OFF,ATP_RX_FLOW_UNMASK); 

				rvf_free_buf (rx_packet_p);
				rvf_free_buf (rvf_buffer_p);
				return RV_NOT_SUPPORTED;
		   }		
		   data_ready_p->rv_hdr.msg_id=ATP_DATA_RDY;
		   data_ready_p->port_nb=other_port_info_p->port_nb;
		   data_ready_p->nb_bytes=real_buffer_size;
 
		   /* Queue the packet */
		   rvf_enqueue (&(other_port_info_p->rx_queue), rx_packet_p);
		   other_port_info_p->rx_data_left += real_buffer_size;

#ifdef ATP_DEBUG_MSG_ENABLED
			(void) sprintf (gbl_debug_message,
							"ATP: DATA_RDY sent (Port: %d, Size [Packet]: %d, Left [Overall]: %d) ",
							data_ready_p->port_nb,
							data_ready_p->nb_bytes,
							other_port_info_p->rx_data_left);
			rvf_send_trace (gbl_debug_message,
							(UINT8) strlen (gbl_debug_message),
							NULL_PARAM,
							RV_TRACE_LEVEL_DEBUG_MEDIUM,
							ATP_USE_ID);
#endif

		   /* Send the event */
		   if (atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *)data_ready_p) != RV_OK)
		   {
				atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_PARAM_ERROR,NULL);
				return RV_INTERNAL_ERR;
		   }
		   *nb_bytes_left_p=0;
		   return RV_OK;
		}
		default:
		{
			break;
		}
	}
	atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_OTHER_SWE_NOT_IN_PROPER_MODE,NULL);
	rvf_free_buf (rvf_buffer_p);
	return RV_NOT_SUPPORTED;
}








/******************************************************************************
* Function name: atp_no_copy_send_data
*
* Description : Send data on a port.
*
* Parameters :  see BT9901 : 
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*
*					atp_error can be called if MB is RED 
*
* History		: 0.1 (1-Marsh-2000) - Created
*				: 0.9  (5-May-2000) - Pas au point !!!!!!!
******************************************************************************/
T_ATP_RET atp_no_copy_send_data(T_ATP_SW_ENTITY_ID sender_sw_id, T_ATP_PORT_NB sender_port_nb,
								T_ATP_BUFFER atp_buffer_p, UINT32 payload_size)
{
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB sender_sw_nb;
	T_ATP_SW_ENTITY_STRUCT * other_sw_entity_p;
	T_ATP_NO_COPY_DATA_RDY * no_copy_data_ready_p;
	T_ATP_DATA_RDY *data_ready_p;
	T_ATP_RX_PACKET *rx_packet_p;
	UINT32 rx_head,tx_head,rx_trail,tx_trail,tx_data_size,i,data_length,tx_buffer_size;
	T_ATP_BUFFER tx_buffer_p;
	T_ATP_PORT_END_STRUCT * other_port_info_p;
	T_RVF_MB_STATUS mb_status;
	UINT8 switch_memory;
	T_ATP_SW_NB other_sw_nb;
	T_ATP_ESCAPE_SEQUENCE_STATUS escape_status; // status indicating if the escape sequence has been found in the data flow
	UINT8 nb_escape_extra_character; // Number of character of the escape sequence which has been already found

#ifdef ATP_DEBUG_MSG_ENABLED
	(void) sprintf (gbl_debug_message,
					"ATP: no_copy_send_data invoked (Size: %d, Port: %d) ",
					payload_size,
					sender_port_nb);
	rvf_send_trace (gbl_debug_message,
					(UINT8) strlen (gbl_debug_message),
					NULL_PARAM,
					RV_TRACE_LEVEL_DEBUG_MEDIUM,
					ATP_USE_ID);
#endif

	/* Get the pointer on the port structure */
	if(atp_get_port(sender_sw_id,sender_port_nb,&port_p,&sender_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	other_sw_nb=(T_ATP_SW_NB) (! sender_sw_nb);
	
	if (port_p->port_state == ATP_OPEN_PENDING)
	{
		/* Port is not completely open */
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_ISSUED_IN_A_WRONG_STATE_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	/* Get other port */
	if ((port_p->redirect_mode==ATP_REDIRECT_ON) && 
		(port_p->port_state==ATP_DATA_MODE)) /* redirection is activated */
	{
		other_port_info_p= & (port_p->redirect_port_p->port_info[port_p->redirect_port_end_nb]);
	}
	else
	{
		other_port_info_p= & (port_p->port_info[(! sender_sw_nb)]);
	}
	
	
	/* get pointer on the other SW entity data structure in order to get info */
	other_sw_entity_p=atp_sw_entity_table_p[(* other_port_info_p).sw_id];
	
	/* Get info on rx and tx value => to make the code more readable */
	rx_head=other_port_info_p->no_copy_info.rx_head_size;
	rx_trail=other_port_info_p->no_copy_info.rx_trail_size;
	tx_head=port_p->port_info[sender_sw_nb].no_copy_info.tx_head_size;
	tx_trail=port_p->port_info[sender_sw_nb].no_copy_info.tx_trail_size;
	
	
	
	/* If sender is a transport layer and if ATP needs to emulate DCE or DTE 
	then interpret the data and send proper command to other SWE */
	if ((atp_sw_entity_table_p[sender_sw_id]->mode.cmd_support_mode == CMD_SUPPORT_OFF) &&
		 ( (port_p->port_state!=ATP_DATA_MODE) && (port_p->port_config != DATA_CONFIG)))
	{ 
		T_RV_RET return_status;
		T_ATP_CMD_BUFFER_RDY is_ready;		
		
		/* Allocate text buffer */
		if (rvf_get_buf(other_port_info_p->rx_mb,payload_size+1,(void **) &tx_buffer_p)==RVF_RED)
		{
			atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_MEMORY_ERROR,NULL);
			return RV_MEMORY_ERR;
		}
		
		/* And update txt buffer with the data contained in the received packet */
		/* Packet in segment mode (cf L2CAP) ? */
		if (port_p->port_info[sender_sw_nb].no_copy_info.packet_mode==SEGMENTED_PACKET)
		{
			tx_data_size=payload_size+1; /* + 1 for 0 at the end */
			atp_copy_buffer_from_l2cap((void *) atp_buffer_p,(void *) tx_buffer_p,payload_size,0);
			tx_buffer_p[payload_size]=0; /* End of the String */
			atp_free_l2cap_buffer((UINT8 *)atp_buffer_p);
		}
		else
		{
			tx_data_size=payload_size;
			atp_copy_buffer(&atp_buffer_p[tx_head],tx_buffer_p,(UINT32) tx_data_size);
			rvf_free_buf(atp_buffer_p); /* Release data buffer : text is now pointed by txt_cmd_p */
			
		}
		
		/* Command is stored in tx_buffer_p . tx_length (without 0 at end) = payload_size */

		if (port_p->port_config == DCE_CONFIG) // ATP is emulating DCE 
		{
			// DCE accept several AT commands to be assembled on the same line 
			// In case ATP DCE receives new commands where ATP has not finished to provide
			// all the commands to SWE -> ignore new command
			if (port_p->cmd_info.status == ON_GOING)
			{ 
				ATP_SEND_TRACE ("ATP/DCE : Receives new commands from DTE whereas previous line has not been processed : ignore ",RV_TRACE_LEVEL_WARNING);
				//				port_p->cmd_info.status = FINISHED;
				//				if (port_p->cmd_info.cmd_txt_p != NULL)
				//				{
				//					rvf_free_buf(port_p->cmd_info.cmd_txt_p);
				//				}
				//				port_p->cmd_info.cmd_txt_p=NULL;
				//				port_p->cmd_info.next_position=ATP_CMD_INVALID_POSITION;
				//				port_p->cmd_info.cmd_txt_length=0;
				
				// SHALL WE SEND A RESULT TO DTE ???????
				rvf_free_buf((UINT8 *) tx_buffer_p);
				return RV_OK;
			}
			
			
			/* Check if mode Echo is ON */
			/* In case ECHO mode is activated and DCE , re-send the packet to sender */
			if (port_p->dce_info_p->echo_mode == ECHO_ON)
			{
				if (atp_send_data(other_port_info_p->sw_id,other_port_info_p->port_nb,
					tx_buffer_p,strlen((char *) tx_buffer_p),&data_length) != RV_OK)
				{
					ATP_SEND_TRACE ("ATP : Failed to send command back in echo mode",
						RV_TRACE_LEVEL_ERROR);
				}
			}
		} /* End of if ATP = DCE */
			
			
		/* Update internal cmd buffer: especially, in case cmd is sent character per character */
		/* In this case, this function gathers the caracter . is_ready = ATP_CMD_BUFFER_IS _RDY	*/
		/* once a complete command has been received . In this case, the command and related information */
		/* is available in port_p->cmd_info structure. */
		return_status = atp_update_cmd_buffer(port_p,tx_buffer_p,(UINT16) payload_size,&is_ready);

		/* Error in the data received. Sends an error to the remote device. */
		if (return_status != RV_OK)
		{
			port_p->cmd_info.status=FINISHED; // will not get any new command
			atp_send_cmd(port_p->port_info[other_sw_nb].sw_id,port_p->port_info[other_sw_nb].port_nb,
				RESULT_CODE,ATP_ERROR_NB,NULL);
			atp_init_cmd_info_struct(port_p);
			return RV_OK;
		}

		if (is_ready == ATP_CMD_BUFFER_IS_NOT_RDY)
		{
			/* Wait for following characters */
			return RV_OK;
		}


		/* Let's start to process the command  */
		return_status = atp_interpret_data(port_p,sender_sw_nb,other_sw_nb); // sender = transport layer , other = appli
						  
		if (return_status == RV_MEMORY_ERR)
		{
			atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_MEMORY_ERROR,NULL);
			return RV_MEMORY_ERR;
		}
		
		return RV_OK;		
	} /* End of DCE Emu part */
	
	
	
				
	
	
	/* Data copy */
	/*---------------------------------------------------------------------*/
	
	   /* First case: Target needs a copy */
	   data_length=payload_size; /* Nb of bytes of the real data (not including header...) */

	   /* Check ESCAPE SEQUENCE if DCE mode enabled */
	   if ( (atp_sw_entity_table_p[sender_sw_id]->mode.cmd_support_mode == CMD_SUPPORT_OFF) &&
		   (port_p->port_config == DCE_CONFIG))
	   {
		   // In this case, need to check escape sequence 
		   escape_status = atp_escape_sequence_process(port_p,atp_buffer_p,data_length,
			   port_p->port_info[sender_sw_nb].no_copy_info.packet_mode);
		   nb_escape_extra_character = port_p->dce_info_p->nb_plus_received; // Number of escape sequence character already received

		   if (escape_status == ATP_ESCAPE_SEQUENCE_SUCCESS)
		   {
			   // Escape sequence has been found
			   ATP_SEND_TRACE ("ATP: An escape sequence has been found. Send CMD_ABORT ",RV_TRACE_LEVEL_DEBUG_LOW);
			   atp_send_cmd(sender_sw_id,sender_port_nb,CMD_ABORT,0,NULL);
			   atp_reset_escape_sequence(port_p); // Delete all buffer including current atp_buffer_p 			   
		   }
		   if (escape_status != ATP_ESCAPE_SEQUENCE_FAILED)
		   {
			   return RV_OK; // If SUCCESS or WAIT, no data need to be forwarded
		   }
	   }
	   else
	   {
		   nb_escape_extra_character = 0; 
	   }
		   



	   if(other_sw_entity_p->mode.cp_mode==COPY_ON)
	   {
		   /* So Queue the packet and send an ATP_DATA_RDY event */		
		   /* Get enqueue header */
		   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;
		   }		
		   
		   if (nb_escape_extra_character > 0)
		   {
			 atp_pipe_extra_character(port_p,other_port_info_p); // 	send in the pipe the character that has been 
	
			 // Reset internal structure but does not release data buffer !
			 port_p->dce_info_p->nb_plus_received = 0;
			 for(i=0;i<MAX_NB_OF_CHARACTER_FOR_END_SEQUENCE;i++)
			 {
				 port_p->dce_info_p->escape_sequence_tmp_buffer_p[i] = NULL;
				 port_p->dce_info_p->length_of_escape_sequence_tmp_buffer_p[i] = 0;
			 }
		   }
			   
		   rx_packet_p->first_byte=tx_head;
		   rx_packet_p->last_byte=tx_head+payload_size-1;
		   rx_packet_p->atp_buffer_p=atp_buffer_p;
		   rx_packet_p->next_byte_to_read=rx_packet_p->first_byte;

		   /* Queue the packet */
		   rvf_enqueue (&(other_port_info_p->rx_queue), rx_packet_p);
		   other_port_info_p->rx_data_left+=payload_size;
		   
		   /* Get a ATP_DATA_RDY event */
		   if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_DATA_RDY),(void **) &data_ready_p)==RVF_RED)
		   {
			   atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_MEMORY_ERROR,NULL);
				return RV_MEMORY_ERR;
		   }		
		   
		   data_ready_p->rv_hdr.msg_id=ATP_DATA_RDY;
		   data_ready_p->port_nb=other_port_info_p->port_nb;
		   data_ready_p->nb_bytes=payload_size;

#ifdef ATP_DEBUG_MSG_ENABLED
			(void) sprintf (gbl_debug_message,
							"ATP: DATA_RDY sent (Port: %d, Size [Packet]: %d, Left [Overall]: %d) ",
							data_ready_p->port_nb,
							data_ready_p->nb_bytes,
							other_port_info_p->rx_data_left);
			rvf_send_trace (gbl_debug_message,
							(UINT8) strlen (gbl_debug_message),
							NULL_PARAM,
							RV_TRACE_LEVEL_DEBUG_MEDIUM,
							ATP_USE_ID);
#endif
 
		   /* Send the event */
		   if (atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *)data_ready_p) != RV_OK)
		   {
				atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_PARAM_ERROR,NULL);
				return RV_INTERNAL_ERR;
		   }
		   return RV_OK;
	   }
	   
	   
	   
	   
	   /* Second case : Target is COPY OFF but packet features are not compatible => needs a copy */
	   if( (tx_head!=rx_head) || (tx_trail!=rx_trail) ||
		   ( (port_p->port_info[sender_sw_nb].no_copy_info.packet_mode ==  SEGMENTED_PACKET) &&
		   (other_port_info_p->no_copy_info.packet_mode !=  SEGMENTED_PACKET) )  ) 
	   {		
		   tx_data_size=payload_size+nb_escape_extra_character;
		   tx_buffer_size=tx_data_size+rx_head+rx_trail;
		   
		   
		   /* Data will be copied into a buffer which will be forwarded to upper layers  */
		   /* Get this buffer from the port MB. If not enough memory available then use 
		   ATP_MB, make the copy , release the initial buffer and try to re-switch the buffer
		   into the port MB (use of swicth_memory for that purpose */
		   
		   switch_memory=0; /* Ie no needs to switch from ATP_MB to port MB */
		   mb_status=rvf_get_buf(other_port_info_p->rx_mb,tx_buffer_size,(void **) &tx_buffer_p);
		   if (mb_status==RVF_RED)
		   {
			   /* Then use own atp_prim MB.... for temporary work ! */
			   mb_status=rvf_get_buf(atp_mb_prim,tx_buffer_size,(void **) &tx_buffer_p);
			   switch_memory=1; /* Switch is needed */
			   if (mb_status==RVF_YELLOW)
			   {
				   /* Then , sender should definitely stop to send data !!!  */
				   atp_set_signal(other_port_info_p->sw_id,other_port_info_p->port_nb,
					   ATP_TX_FLOW_OFF,ATP_TX_FLOW_UNMASK);
			   }
			   
			   if (mb_status==RVF_RED)
			   {
				   /* Even no memory enough in atp_mb_prim -> big problem */
				   rvf_send_trace("ATP : not enough memeory for temporary copy from COPY_OFF to COPY OFF SWEs (atp_no_copy_send_data ft)  ",
					   103,
					   NULL_PARAM,
					   RV_TRACE_LEVEL_ERROR,
					   ATP_USE_ID);
				   
				   atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_MEMORY_ERROR,NULL);
				   return RV_MEMORY_ERR;
				   
			   }
			   
		   }
		   
		   if (nb_escape_extra_character>0)
		   {
			   // Add character that has been removed due to escape sequence scanning
			   memcpy(&tx_buffer_p[rx_head],port_p->dce_info_p->escape_sequence,
				   nb_escape_extra_character);
			   atp_reset_escape_sequence(port_p);  
		   }
		   
		   if (port_p->port_info[sender_sw_nb].no_copy_info.packet_mode==NORMAL_PACKET)
		   {   /* Packet is not a L2CAP segmented packet */
			   atp_copy_buffer(&atp_buffer_p[tx_head],&tx_buffer_p[rx_head+nb_escape_extra_character]
				   ,(tx_data_size-nb_escape_extra_character));
			   rvf_free_buf(atp_buffer_p);
			   
		   }
		   else
		   {	/* Sender Packet was in segmented */
			   atp_copy_buffer_from_l2cap((void *)atp_buffer_p,(void *) &tx_buffer_p[rx_head+nb_escape_extra_character],
				   (UINT32)(tx_data_size-nb_escape_extra_character),0);
			   atp_free_l2cap_buffer( (UINT8 *)atp_buffer_p);
			   
		   }
		   
		   /* Copy dummy byte */
		   for(i=0;i<rx_head;i++)
		   {
			   tx_buffer_p[i]=0x00;
		   }
		   
		   for(i=tx_data_size+rx_head;i<tx_buffer_size;i++)
		   {
			   tx_buffer_p[i]=0x00;
		   }
		   
		   /* Release previous buffer */
		   atp_buffer_p=tx_buffer_p; /* Update to send the data */
		   data_length=tx_data_size;	
		   
		   /* And switch to port MB if possible */
		   if (switch_memory==1)
		   {
			   rvf_count_buf(other_port_info_p->rx_mb,tx_buffer_p);
		   }
		   
		   
	   }
	   else
	   {
		   /* else => a single forward is possible if same packet requirements (RVF/header/trailer/packet mode)*/

		   if (nb_escape_extra_character>0)
		   {
			   // Send characters that has been removed due to escape sequence scanning
			   UINT8 * buffer_p;
			   
			   if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_NO_COPY_DATA_RDY),(void **) &no_copy_data_ready_p)==RVF_RED)
			   {
				   atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_MEMORY_ERROR,NULL);
				   return RV_MEMORY_ERR;
			   }		
			   if (rvf_get_buf(atp_mb_prim,nb_escape_extra_character,(void **) &buffer_p)==RVF_RED)
			   {
				   rvf_free_buf (no_copy_data_ready_p);
				   atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_MEMORY_ERROR,NULL);
				   return RV_MEMORY_ERR;
			   }
			   // Copy data into the buffer
			   memcpy(buffer_p,port_p->dce_info_p->escape_sequence,nb_escape_extra_character);
			   
			   // And send
			   no_copy_data_ready_p->rv_hdr.msg_id=ATP_NO_COPY_DATA_RDY;
			   no_copy_data_ready_p->port_nb=other_port_info_p->port_nb;
			   no_copy_data_ready_p->buffer_size=nb_escape_extra_character;
			   no_copy_data_ready_p->atp_buffer_p=buffer_p;

#ifdef ATP_DEBUG_MSG_ENABLED
				(void) sprintf (gbl_debug_message,
								"ATP: NO_COPY_DATA_RDY sent (Port: %d, Size: %d) ",
								no_copy_data_ready_p->port_nb,
								no_copy_data_ready_p->buffer_size);
				rvf_send_trace (gbl_debug_message,
								(UINT8) strlen (gbl_debug_message),
								NULL_PARAM,
								RV_TRACE_LEVEL_DEBUG_MEDIUM,
								ATP_USE_ID);
#endif

			   /* Send the event */
			   atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *) no_copy_data_ready_p);
			   
			   // Reset temporary data used by the escape sequence algo
			   atp_reset_escape_sequence(port_p);  
		   }
	   }




	   /* Send a ATP_NO_COPY_DATA_RDY event with the data*/
	   if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_NO_COPY_DATA_RDY),(void **) &no_copy_data_ready_p)==RVF_RED)
	   {
		   atp_error_switch(ATP_ERROR_FAILED_TO_SEND_DATA,ATP_MEMORY_ERROR,NULL);
		   return RV_MEMORY_ERR;
	   }		
	   
	   no_copy_data_ready_p->rv_hdr.msg_id=ATP_NO_COPY_DATA_RDY;
	   no_copy_data_ready_p->port_nb=other_port_info_p->port_nb;
	   no_copy_data_ready_p->buffer_size=data_length;
	   no_copy_data_ready_p->atp_buffer_p=atp_buffer_p;

#ifdef ATP_DEBUG_MSG_ENABLED
		(void) sprintf (gbl_debug_message,
						"ATP: NO_COPY_DATA_RDY sent (Port: %d, Size: %d) ",
						no_copy_data_ready_p->port_nb,
						no_copy_data_ready_p->buffer_size);
		rvf_send_trace (gbl_debug_message,
						(UINT8) strlen (gbl_debug_message),
						NULL_PARAM,
						RV_TRACE_LEVEL_DEBUG_MEDIUM,
						ATP_USE_ID);
#endif
	   
	   /* Send the event */
	   atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *) no_copy_data_ready_p);
	   return RV_OK;			
}




/******************************************************************************
* Function name: atp_get_data
*
* Description : Copy data in the SW entity buffer
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*
*					atp_error can be called if MB is RED 
*
* History		: 0.1 (1-Marsh-2000) - Created
*				: 0.9  (5-May-2000) - Added L2CAP packet support from sender
******************************************************************************/
T_ATP_RET atp_get_data(T_ATP_SW_ENTITY_ID receiver_sw_id, T_ATP_PORT_NB receiver_port_nb,
					   UINT8 * data_buffer, UINT32 nb_to_read, UINT32 *nb_read_p, 
					   UINT32 *nb_left_p)
{
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB receiver_sw_nb;
	T_ATP_RX_PACKET *rx_packet_p;
	UINT32 start_index,nb_to_copy;
    T_ATP_PACKET_MODE packet_mode;

#ifdef ATP_DEBUG_MSG_ENABLED
	(void) sprintf (gbl_debug_message,
					"ATP: get_data invoked (Size: %d, Port: %d) ",
					nb_to_read,
					receiver_port_nb);
	rvf_send_trace (gbl_debug_message,
					(UINT8) strlen (gbl_debug_message),
					NULL_PARAM,
					RV_TRACE_LEVEL_DEBUG_MEDIUM,
					ATP_USE_ID);
#endif

	/* Get the pointer on the port structure */
	if(atp_get_port(receiver_sw_id,receiver_port_nb,&port_p,&receiver_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_GET_DATA,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	
	if (port_p->port_state == ATP_OPEN_PENDING)
	{
		/* Port is not completely open */
		atp_error_switch(ATP_ERROR_FAILED_TO_GET_DATA,ATP_ISSUED_IN_A_WRONG_STATE_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	/* Get information on the format of the data to copy */
	if ((port_p->redirect_mode==ATP_REDIRECT_ON) && 
		(port_p->port_state==ATP_DATA_MODE)) /* redirection is activated */
	{
		packet_mode= port_p->redirect_port_p->port_info[port_p->redirect_port_end_nb].no_copy_info.packet_mode;
	}
	else
	{
		packet_mode=port_p->port_info[(! receiver_sw_nb)].no_copy_info.packet_mode; /* packet mode of the sender */
	}
	*nb_read_p=0; // No data has been copied yet 

	/* Check number of data available */
	if ((port_p->port_info[receiver_sw_nb].rx_data_left == 0) || \
		(RVF_IS_QUEUE_EMPTY((port_p->port_info[receiver_sw_nb].rx_queue))))
	{   /* No pending packet are available in the RX queue */
		*nb_left_p=0;

#ifdef ATP_DEBUG_MSG_ENABLED
	(void) sprintf (gbl_debug_message,
					"ATP: get_data (Read: %d, Left [Packet]: %d, Left [Overall]: %d) ",
					*nb_read_p,
					*nb_left_p,
					port_p->port_info[receiver_sw_nb].rx_data_left);
	rvf_send_trace (gbl_debug_message,
					(UINT8) strlen (gbl_debug_message),
					NULL_PARAM,
					RV_TRACE_LEVEL_DEBUG_MEDIUM,
					ATP_USE_ID);
#endif
		return RV_OK;
	}

	// TO UPDATE IN ORDER TO COPE WITH L2CAP PACKETS !!!!!
	rx_packet_p=(T_ATP_RX_PACKET *) rvf_dequeue (&(port_p->port_info[receiver_sw_nb].rx_queue));
	start_index=rx_packet_p->next_byte_to_read; /* Next byte can start at [0] */
	nb_to_copy=Min(rx_packet_p->last_byte-start_index+1,nb_to_read); /* Number of byte from this packet to copy */

	if (packet_mode==NORMAL_PACKET)
	{ /* Stored Data is in a single buffer */
		atp_copy_buffer(&(rx_packet_p->atp_buffer_p[start_index]),&(data_buffer[*nb_read_p]),nb_to_copy);
	}
	else
	{
		atp_copy_buffer_from_l2cap(rx_packet_p->atp_buffer_p,&data_buffer[*nb_read_p],nb_to_copy,start_index);
	}
			
	/* Update counters */
	port_p->port_info[receiver_sw_nb].rx_data_left-=nb_to_copy;	/* Overall number of bytes */
																/* left to be read */
	rx_packet_p->next_byte_to_read=rx_packet_p->next_byte_to_read+nb_to_copy;

	*nb_read_p=nb_to_copy; /* Number of byte read */
	*nb_left_p=rx_packet_p->last_byte+1-rx_packet_p->next_byte_to_read;	/* Number of bytes left */
																		/* to be read */

#ifdef ATP_DEBUG_MSG_ENABLED
	(void) sprintf (gbl_debug_message,
					"ATP: get_data (Read: %d, Left [Packet]: %d, Left [Overall]: %d) ",
					*nb_read_p,
					*nb_left_p,
					port_p->port_info[receiver_sw_nb].rx_data_left);
	rvf_send_trace (gbl_debug_message,
					(UINT8) strlen (gbl_debug_message),
					NULL_PARAM,
					RV_TRACE_LEVEL_DEBUG_MEDIUM,
					ATP_USE_ID);
#endif

	if (*nb_left_p < 1)
	{
		/* Buffer has been entirely copied : free buffer */
		if (packet_mode==NORMAL_PACKET)
		{
			rvf_free_buf(rx_packet_p->atp_buffer_p);
		}
		else
		{
			atp_free_l2cap_buffer ((UINT8 *) rx_packet_p->atp_buffer_p);
		}
		rvf_free_buf(rx_packet_p);
		return RV_OK;
	}

	/* In this case, still some data need to be read from the RX packet */
	/* Re-enqueue the buffer and go out of the while  loop */
	rvf_enqueue_head(&(port_p->port_info[receiver_sw_nb].rx_queue),rx_packet_p);
	return RV_OK;
}








/******************************************************************************
* Function name: atp_set_mode
*
* Description : Change the mode of the port
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*
*					atp_error can be called if MB is RED 
*
* History		: 0.1 (09-May-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_set_mode(T_ATP_SW_ENTITY_ID sender_sw_id, T_ATP_PORT_NB sender_port_nb,
					   T_ATP_PORT_MODE mode)
{
	
	
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB sender_sw_nb, other_sw_nb;
	T_ATP_SW_ENTITY_STRUCT * other_sw_entity_p;
	T_ATP_PORT_MODE_CHANGED * mode_changed_p;
	
	
	/* Get the pointer on the port structure */
	if(atp_get_port(sender_sw_id,sender_port_nb,&port_p,&sender_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_HANDLE_MODE,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	other_sw_nb=(T_ATP_SW_NB) (! sender_sw_nb);
	
	if (port_p->port_state == ATP_OPEN_PENDING)
	{
		/* Port is not completely open */
		atp_error_switch(ATP_ERROR_FAILED_TO_HANDLE_MODE,ATP_ISSUED_IN_A_WRONG_STATE_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	/* get pointer on the other SW entity data structure in order to get info */
	other_sw_entity_p=atp_sw_entity_table_p[port_p->port_info[other_sw_nb].sw_id];
	
	
	/* Change the mode of the port */
	if (mode==ATP_PORT_DATA_MODE)
	{
		port_p->port_state=ATP_DATA_MODE;
	}
	else
	{
		port_p->port_state=ATP_CMD_MODE;
		// And reset the cmd_info field
		atp_init_cmd_info_struct(port_p);		
	}
	
	
	
	/* And send an event to the other SW entity if the other is not a TL ie does not support commands  
	(otherwise, mode switch is completely transmparent ...*/
	if (other_sw_entity_p->mode.cmd_support_mode != CMD_SUPPORT_OFF)
	{
		if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_PORT_MODE_CHANGED),(void **) &mode_changed_p)==RVF_RED)
		{
			atp_error(ATP_ERROR_MB_PRIM_RED);
			return (RV_MEMORY_ERR);
		}		
		
		mode_changed_p->rv_hdr.msg_id=ATP_PORT_MODE_CHANGED;
		mode_changed_p->port_nb=port_p->port_info[other_sw_nb].port_nb;
		mode_changed_p->mode=mode;
		
		
		/* Send the event */
		atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *)mode_changed_p);
	}
	else
	{
		// Other SWE does not support command : So, is a DCE ?
		if (port_p->port_config == DCE_CONFIG)
		{
			// Reset fields used by the escape sequence algorithm 
			atp_reset_escape_sequence(port_p);
		}
	}
	
	return RV_OK;
}

/******************************************************************************
* Function name: atp_set_signal
*
* Description : Set signal value of the port
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*
*					atp_error can be called if MB is RED 
*
* History		: 0.1 (1-Marsh-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_set_signal(T_ATP_SW_ENTITY_ID sender_sw_id, T_ATP_PORT_NB sender_port_nb,
						 T_ATP_PORT_SIGNAL set_signal, T_ATP_SIGNAL_CHANGE_MASK set_mask)
{
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB sender_sw_nb, other_sw_nb;
	T_ATP_SW_ENTITY_STRUCT * other_sw_entity_p;
	UINT8  signal_changed;
	T_ATP_SIGNAL_CHANGED *  signal_changed_p;
	T_ATP_PORT_SIGNAL sender_signal,other_signal,set_signal_value;
	T_ATP_SIGNAL_CHANGE_MASK get_mask,new_mask; /* Mask on the signal changed for the other SW entity */
	T_ATP_PORT_END_STRUCT * other_port_info_p;
	BOOLEAN  wait_for_mb_callback;

	/* Get the pointer on the port structure */
	if(atp_get_port(sender_sw_id,sender_port_nb,&port_p,&sender_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_HANDLE_SIGNAL,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}

	/* Get pointer on the other SW entity data structure in order to get info */
	/* Get other port taking care on redirection*/
	if ((port_p->redirect_mode==ATP_REDIRECT_ON) && 
		(port_p->port_state==ATP_DATA_MODE)) /* redirection is activated */
	{
		other_port_info_p= & (port_p->redirect_port_p->port_info[port_p->redirect_port_end_nb]);
		other_sw_nb=(T_ATP_SW_NB) (port_p->redirect_port_end_nb);
	}
	else
	{
		other_port_info_p= & (port_p->port_info[(! sender_sw_nb)]);
		other_sw_nb=(T_ATP_SW_NB) (! sender_sw_nb);
	}

	/* Get pointer on the other SW entity data structure in order to get info */
	other_sw_entity_p=atp_sw_entity_table_p[(* other_port_info_p).sw_id];

	if (port_p->port_state == ATP_OPEN_PENDING)
	{
		/* Port is not completely open */
		atp_error_switch(ATP_ERROR_FAILED_TO_HANDLE_SIGNAL,ATP_ISSUED_IN_A_WRONG_STATE_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	sender_signal=port_p->port_info[sender_sw_nb].signal;
	other_signal=other_port_info_p->signal;
	
	get_mask=0;
	wait_for_mb_callback=FALSE;
	
	/* Set RX flow control signal ? */
	if ((set_mask & ATP_RX_FLOW_UNMASK)!=0) /* Sender wants to set its RX flow */
	{	/* Get sender RX bit value */
		set_signal_value = (T_ATP_SIGNAL_MASK)( set_signal &  ATP_RX_FLOW_UNMASK );

		/* set_signal = ATP_RX_FLOW_ON or ATP_RX_FLOW_OFF */
		if (set_signal_value == ATP_RX_FLOW_ON)
		{	/* Caller wants to set ATP_RX_FLOW_CTRL */
			/* Set it into sender signal status and set TX_FLOW_ON on the other port */
			sender_signal |= ATP_RX_FLOW_ON;
			rvf_send_trace ("ATP: RX FLOW set to ON. Port ",
							29,
							sender_port_nb,
							RV_TRACE_LEVEL_DEBUG_LOW,
							ATP_USE_ID);
			if (( other_signal & ATP_TX_FLOW_UNMASK) == ATP_TX_FLOW_OFF) 
			{  /* TX_FLOW_CTRL was OFF for the other entity : set it */
				other_signal |= ATP_TX_FLOW_ON;

				/* A signal will be generated even if it has been issue earlier */
				get_mask |= ATP_TX_FLOW_UNMASK;
			}
		}
		else
		{	/* Caller wants to clear ATP_RX_FLOW_CTRL => RX_FLOW = OFF */
			/* Clear RX bit on the sender */
			sender_signal &= (~ ATP_RX_FLOW_UNMASK);
			rvf_send_trace ("ATP: RX FLOW set to OFF. Port ",
							30,
							sender_port_nb,
							RV_TRACE_LEVEL_DEBUG_LOW,
							ATP_USE_ID);
			wait_for_mb_callback = TRUE;

			/* Other TX bit value = OFF */
			if (( other_signal & ATP_TX_FLOW_UNMASK) == ATP_TX_FLOW_ON)
			{	/* TX_FLOW_CTRL was ON for the other entity : clear it */
				other_signal &= (~ ATP_TX_FLOW_UNMASK);

				/* A signal will be generated even if it has been issue earlier */
				get_mask |= ATP_TX_FLOW_UNMASK; 
			}
		}
	}

	/* Set TX flow control signal ? */
	if ((set_mask & ATP_TX_FLOW_UNMASK)!=0) /* Sender wants to set the TX flow */
	{	/* Get sender TX bit value */
		set_signal_value = (T_ATP_SIGNAL_MASK) (set_signal &  ATP_TX_FLOW_UNMASK);

		/* set_signal = ATP_TX_FLOW_ON or ATP_TX_FLOW_OFF */
		if (set_signal_value == ATP_TX_FLOW_ON)
		{	/* Caller wants to set ATP_TX_FLOW_CTRL */
			/* Set it into sender signal status and set RX_FLOW_ON on the other port */
			sender_signal |= ATP_TX_FLOW_ON;
			rvf_send_trace ("ATP: TX FLOW set to ON. Port ",
							29,
							sender_port_nb,
							RV_TRACE_LEVEL_DEBUG_LOW,
							ATP_USE_ID);
			if (( other_signal & ATP_RX_FLOW_UNMASK) == ATP_RX_FLOW_OFF) 
			{  /* RX_FLOW_CTRL was OFF for the other entity : set it */
				other_signal |= ATP_RX_FLOW_ON;
				get_mask |= ATP_RX_FLOW_UNMASK;
			}
		}
		else
		{	/* Caller wants to clear ATP_TX_FLOW_CTRL */
			/* Clear TX bit on the sender */
			sender_signal &= (~ ATP_TX_FLOW_UNMASK);
			rvf_send_trace ("ATP: TX FLOW set to OFF. Port ",
							30,
							sender_port_nb,
							RV_TRACE_LEVEL_DEBUG_LOW,
							ATP_USE_ID);

			/* Other TX bit value = OFF */
			if (( other_signal & ATP_RX_FLOW_UNMASK) == ATP_RX_FLOW_ON)
			{  /* RX_FLOW_CTRL was ON for the other entity : clear it*/
				other_signal &= (~ ATP_RX_FLOW_UNMASK);
				get_mask |= ATP_RX_FLOW_UNMASK;
			}
		}
	}

	/* Set other signals */
	/* Other signals to handle */
	new_mask= (T_ATP_SIGNAL_MASK) (set_mask & ATP_NON_RX_TX_SIGNAL_UNMASK);

	/* Take only the good signals */
	signal_changed= (T_ATP_SIGNAL_MASK) (set_signal & new_mask);
	
	port_p->port_info->signal= (T_ATP_SIGNAL_MASK) ((sender_signal & (~ new_mask)) | signal_changed);
	other_port_info_p->signal = (T_ATP_SIGNAL_MASK) ((other_signal & (~ new_mask)) | signal_changed);
	get_mask |= new_mask;

	/* Set the callback function to send signal TX_ON later on */
	if (wait_for_mb_callback)
	{

		/* Getting the sendee. */
		port_p->port_waiting_for_mb_callback = sender_sw_nb;
		if (rvf_set_callback_func(port_p->port_info[sender_sw_nb].rx_mb,atp_mb_call_back))
		{
			rvf_change_callback_func( (T_RVF_MB_ID) (port_p->port_info[sender_sw_nb].rx_mb),atp_mb_call_back);
		}
	}
	if (get_mask !=0)
	{	/* Send a ATP_SIGNAL_CHANGED event */
		if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_SIGNAL_CHANGED),(void **) &signal_changed_p)==RVF_RED)
		{
			atp_error(ATP_ERROR_MB_PRIM_RED);
			return (RV_MEMORY_ERR);
		}		
		
		signal_changed_p->rv_hdr.msg_id=ATP_SIGNAL_CHANGED;
		signal_changed_p->mask=get_mask;
		signal_changed_p->port_nb=other_port_info_p->port_nb;
		signal_changed_p->signal=other_port_info_p->signal;
		signal_changed_p->mb=other_port_info_p->tx_mb;

		/* Send the event */
		atp_send_message(other_sw_entity_p->return_path,(T_ATP_MESSAGE *)signal_changed_p);
	}
	return RV_OK;	
}




/******************************************************************************
* Function name: atp_get_signal
*
* Description : Get signal value of the port
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*
*					atp_error can be called if MB is RED 
*
* History		: 0.1 (1-Marsh-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_get_signal(T_ATP_SW_ENTITY_ID sw_id, T_ATP_PORT_NB port_nb,
						 T_ATP_PORT_SIGNAL * signal_p)
{
	
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB sw_nb;
	
	/* Get the pointer on the port structure */
	if(atp_get_port(sw_id,port_nb,&port_p,&sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_HANDLE_SIGNAL,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	
	if (port_p->port_state == ATP_OPEN_PENDING)
	{
		/* Port is not completely open */
		atp_error_switch(ATP_ERROR_FAILED_TO_HANDLE_SIGNAL,ATP_ISSUED_IN_A_WRONG_STATE_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	*signal_p=port_p->port_info[sw_nb].signal;
	
	return RV_OK;	
}








/******************************************************************************
* Function name: atp_flow_redirect
*
* Description : Redirect the flow from one port to another one
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*
*					atp_error can be called if MB is RED 
*
* History		: 0.1 (22-May-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_flow_redirect(T_ATP_SW_ENTITY_ID sw_id, T_ATP_PORT_NB port_nb_1,
							T_ATP_PORT_NB port_nb_2, T_ATP_REDIRECT_MODE redirect_mode)
{
	
	T_ATP_SW_NB sw_nb_1,sw_nb_2,other_sw_nb_1,other_sw_nb_2;
	T_ATP_PORT_STRUCT *port_1_p,*port_2_p;
	
	
	
	/* Find port information */
	
	/* Get the pointer on the port structure related to the port number 1 */
	if(atp_get_port(sw_id,port_nb_1,&port_1_p,&sw_nb_1) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_HANDLE_FLOW_REDIRECTION,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	other_sw_nb_1=(T_ATP_SW_NB) (! sw_nb_1);
	
	/* Get the pointer on the port structure related to the port number 2 */
	if(atp_get_port(sw_id,port_nb_2,&port_2_p,&sw_nb_2) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_HANDLE_FLOW_REDIRECTION,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	other_sw_nb_2=(T_ATP_SW_NB) (! sw_nb_2);
	
	
	// START SEMAPHORE 
	if (redirect_mode == ATP_REDIRECT_ON)
	{
		port_1_p->redirect_mode=ATP_REDIRECT_ON;
		port_2_p->redirect_mode=ATP_REDIRECT_ON;
		
		port_1_p->redirect_port_p=port_2_p;
		port_2_p->redirect_port_p=port_1_p;
		
		port_1_p->redirect_port_end_nb=other_sw_nb_2;
		port_2_p->redirect_port_end_nb=other_sw_nb_1;
		
		// END SEMAPHORE 
		
		return RV_OK;
	}
	
	
	/* Else, pass from REDIRECT_ON to REDIRECT_OFF */
	port_1_p->redirect_mode=ATP_REDIRECT_OFF;
	port_2_p->redirect_mode=ATP_REDIRECT_OFF;
	
	port_1_p->redirect_port_p=NULL;
	port_2_p->redirect_port_p=NULL;
	
	port_1_p->redirect_port_end_nb=0;
	port_2_p->redirect_port_end_nb=0;
	
	
	// END SEMAPHORE 
	
	return RV_OK;
}









/******************************************************************************
* Function name: atp_free_message
*
* Description : Generic function service provided to SW entity to free an ATP buffer
*
* Parameters :  pointer on the buffer
*
* Return     :   return of the rvf_free_buf function
*
* History			: 0.1 (1-Marsh-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_free_message(void * buffer_p)

{
	return rvf_free_buf(buffer_p);
}



/******************************************************************************
* Function name: atp_free_buffer
*
* Description : Generic function service provided to SW entity to free an ATP buffer
*
* Parameters :  pointer on the buffer
*
* Return     :   return of the rvf_free_buf function
*
* History			: 0.1 (1-Marsh-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_free_buffer(void * buffer_p)

{
	if (buffer_p != NULL)
	{
		return rvf_free_buf(buffer_p);
	}
	return RV_OK;
}



/******************************************************************************
* Function name: atp_get_buffer
*
* Description : Generic function service provided to SW entity to free an ATP buffer
*
* Parameters :  pointer on the buffer
*
* Return     :   return of the rvf_free_buf function
*
* History			: 0.1 (1-Marsh-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_get_buffer(UINT32 buffer_size,void ** buffer_pp)

{
	if ( rvf_get_buf(atp_mb_prim,buffer_size,(void **) buffer_pp) == RVF_RED)
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_GET_MEMORY,ATP_MEMORY_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	return RV_OK;
}


/******************************************************************************
* Function name: atp_get_buffer_from_tx_mb
*
* Description : Function used to get memory from the tx memory bank of the SWE
*
* Parameters :  
*
* Return     :   return of the rvf_free_buf function
*
* History			: 0.1 (1-Marsh-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_get_buffer_from_tx_mb(T_ATP_SW_ENTITY_ID sender_sw_id,T_ATP_PORT_NB sender_port_nb,
									UINT32 buffer_size,void ** buffer_pp)

{
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB sender_sw_nb;
	T_ATP_PORT_END_STRUCT * other_port_info_p;
	
	/* Get the pointer on the port structure */
	if(atp_get_port(sender_sw_id,sender_port_nb,&port_p,&sender_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_GET_MEMORY,ATP_MEMORY_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	other_port_info_p= & (port_p->port_info[(! sender_sw_nb)]);

	if ( rvf_get_buf(port_p->port_info[sender_sw_nb].tx_mb,buffer_size,buffer_pp)
		== RVF_RED)
	{		
		/* Sends a signal to stop the TX FLOW CONTROL of the sender 
		ie the receiver set its RX_FLOW_CTRL to OFF  */
		atp_set_signal(other_port_info_p->sw_id,other_port_info_p->port_nb,
			ATP_RX_FLOW_OFF,ATP_RX_FLOW_UNMASK); 
		return RV_NOT_SUPPORTED;
	}
	return RV_OK;
}


/******************************************************************************
* Function name: atp_update_cmd_buffer
* Description : This function gathers data used to transmit buffer into
*				the port internal buffer before sending it to interpretation.
*				Especially usefull for character per character AT command 
*				transmission
*
* Parameters :	is_ready indicates if the txt_buffer is ready for interpretation  
* Return     :   return RV_OK if 
*				 or RV_MEMORY_ERR
*
* History			: 0.1 (8-Nov-2000) - Created - Eric
*
******************************************************************************/
T_ATP_RET atp_update_cmd_buffer(T_ATP_PORT_STRUCT	* port_p,
								UINT8 * new_txt_cmd_buffer_p, UINT16 txt_cmd_buffer_length,
								T_ATP_CMD_BUFFER_RDY * is_ready_p)
{
	UINT16 i,j;
	UINT8 position;		
	
	
	/* Check if a full command is available in the new buffer */
	*is_ready_p = ATP_CMD_BUFFER_IS_NOT_RDY;
	for(i=0;i<txt_cmd_buffer_length;i++)
	{
		
		/* Is there a CR characrter ??*/
		if (new_txt_cmd_buffer_p[i] == port_p->dce_info_p->cr_character)  // cr character has been found in the txt command
		{
			if ( (i != 0 ) || (port_p->cmd_info.next_position != 0) ) // and cr is not the first character of the command line
			{
				*is_ready_p = ATP_CMD_BUFFER_IS_RDY; // OK for interpretation
				break;
			}
			
		}
	}
	
	
	/* Check that buffer is not only <lf> with no previously sent character */
	j=0;
	if ( (new_txt_cmd_buffer_p[0] == port_p->dce_info_p->lf_character) &&
		(port_p->cmd_info.next_position == 0))
	{
		j=1; // skip lf if it is the first caracter of a chain ie the last...
	}
	
	/* Process data  */
	if ((txt_cmd_buffer_length - j ) > 0) // there is something to copy
	{
		/* if buffer does not exist, create it */
		if (port_p->cmd_info.cmd_txt_p == NULL)
		{
			if ( rvf_get_buf(atp_mb_prim,ATP_MAX_CMD_LENGTH,(void **) &port_p->cmd_info.cmd_txt_p) == RVF_RED)
			{
				atp_error_switch(ATP_ERROR_FAILED_TO_ACCEPT_A_PORT,ATP_MEMORY_ERROR,NULL);
				return RV_MEMORY_ERR;
			}
		}
		
		/* Copy buffer */
		position = 	(UINT8) port_p->cmd_info.next_position; // is next position to write on
		
		for(i=j;i<txt_cmd_buffer_length;i++)
		{
			if ( (new_txt_cmd_buffer_p[i] == 	port_p->dce_info_p->bs_character) && // Back space character 
				 (port_p->port_config == DCE_CONFIG)) // And ATP emulates a DCE 
			{
				/* A backspace character has been sent */
				if (position > 0)
				{
					position--;
					port_p->cmd_info.cmd_txt_p[position] = 	' ';
				}
				
			}
			else
			{
				port_p->cmd_info.cmd_txt_p[position] = 	new_txt_cmd_buffer_p[i];		
				if (++position == ATP_MAX_CMD_LENGTH)
				{
					ATP_SEND_TRACE ("ATP : The command received by ATP is too long versus ATP buffer length",RV_TRACE_LEVEL_WARNING);
					return RV_NOT_SUPPORTED;
				}
			}
		}
		
		if (*is_ready_p == ATP_CMD_BUFFER_IS_RDY)
		{
			
			port_p->cmd_info.cmd_txt_p[position] = 0; // Set 0 at the end of the chain
			ATP_SEND_TRACE ("ATP CMD : cmd buffer is ready to be interpreted = ",RV_TRACE_LEVEL_DEBUG_MEDIUM);
			rvf_send_trace(port_p->cmd_info.cmd_txt_p,(UINT8) strlen(port_p->cmd_info.cmd_txt_p),NULL_PARAM,
				RV_TRACE_LEVEL_DEBUG_MEDIUM,ATP_USE_ID);
			
			// Ready for interpretation
			port_p->cmd_info.cmd_txt_length = (UINT8) (position); 
			port_p->cmd_info.next_position = 0; 
			port_p->cmd_info.status = NOT_STARTED;
		}
		else
		{
			// Waiting for new characters
			port_p->cmd_info.next_position = (UINT8) (position); 
			
			// Tracing
			port_p->cmd_info.cmd_txt_p[port_p->cmd_info.next_position] = 0;
			ATP_SEND_TRACE ("ATP CMD : cmd buffer in the pipe = ",RV_TRACE_LEVEL_DEBUG_LOW);
			rvf_send_trace(port_p->cmd_info.cmd_txt_p,(UINT8) strlen(port_p->cmd_info.cmd_txt_p),NULL_PARAM,
				RV_TRACE_LEVEL_DEBUG_LOW,ATP_USE_ID);
		}
		
		
	}
	
	
	
	rvf_free_buf(new_txt_cmd_buffer_p);
	
	return RV_OK;
}






/******************************************************************************
* Function name: atp_interpret_data
* Description : This function is called when DATA received on a transport layer
*				must be interpreted in order to emelate a DCE behaviour
*				The command are either interpreted directly by DCE
*				or sent in INTERPRETED or TXT format to the appli
*				If end of the buffer to interpret is reached, send a OK result
*				code to remote DTE
*				If an error is encountered in the buffer, send a ERROR result
*				code to the remote DTE
*
* Parameters :  pointer on port structure 
*				memory bank to use
*				spp_sw_id = sw id of the transport layer SW entity
*				spp_port_nb = port_nb related to spp_sw_id
*				appli_sw_id = sw id of the appli SW entity
*				appli_port_nb = port_nb related to appli_sw_id
*
* Return     :   return RV_OK
*				 or RV_MEMORY_ERR
*
* History			: 0.1 (1-Marsh-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_interpret_data(T_ATP_PORT_STRUCT	*port_p,
							 T_ATP_SW_NB spp_sw_nb, T_ATP_SW_NB appli_sw_nb)
							 
{
	T_ATP_TXT_CMD new_txt_cmd_p;
	UINT16  txt_length;
	T_ATP_CMD_TYPE	cmd_type;
	T_ATP_CMD_NB	cmd_nb;
	T_ATP_CMD	*cmd_info_p;
	T_ATP_RET return_status;
	
	return_status = atp_interpret_raw_data(port_p,port_p->port_info[appli_sw_nb].rx_mb,
		&cmd_type,&cmd_nb,&cmd_info_p,&new_txt_cmd_p,&txt_length);
	
	
	if (return_status == RV_MEMORY_ERR)
	{
		return RV_MEMORY_ERR;
	}
	
	if (return_status == RV_OK)
	{
		// Check if a command need to be sent
		if ((cmd_type != UNKNOWN) && (new_txt_cmd_p == NULL))
		{
			if ( (port_p->port_config == DTE_CONFIG) && (cmd_type == AT_CMD))
			{
				/* If the command is a AT_CMD whereas ATP is emulating a DTE, then it is certainly 
				because it is the echo of the command that DTE sent previously */
				atp_send_cmd(port_p->port_info[spp_sw_nb].sw_id,port_p->port_info[spp_sw_nb].port_nb,
				PRELIMINARY_RESULT_CODE,cmd_nb,cmd_info_p);
			}
			else
			{
				atp_send_cmd(port_p->port_info[spp_sw_nb].sw_id,port_p->port_info[spp_sw_nb].port_nb,
				cmd_type,cmd_nb,cmd_info_p);
			}
		}
		else
		{
			if (new_txt_cmd_p != NULL)
			{
				atp_send_txt_cmd(port_p->port_info[spp_sw_nb].sw_id,port_p->port_info[spp_sw_nb].port_nb,
					cmd_type,new_txt_cmd_p);
			}
			else
			{
				// In this case, last command has been properly interpreted by DCE and
				// status has been set to FINISHED by interprete raw data
				// DCE must sends a result OK to remote device
				if (port_p->cmd_info.status != FINISHED)
				{
					rvf_send_trace("ATP : status state invalid from interpret_raw_data function ",60,
						NULL_PARAM,RV_TRACE_LEVEL_WARNING,ATP_USE_ID);
					
					port_p->cmd_info.status = FINISHED;
				}
				
				atp_send_cmd(port_p->port_info[appli_sw_nb].sw_id,port_p->port_info[appli_sw_nb].port_nb,
					RESULT_CODE,ATP_OK_NB,NULL);
			}
		}
	}
	else
	{
		// Error in the data received 
		// Sends an error to the remote device
		port_p->cmd_info.status=FINISHED; // will not get any new command
		atp_send_cmd(port_p->port_info[appli_sw_nb].sw_id,port_p->port_info[appli_sw_nb].port_nb,
			RESULT_CODE,ATP_ERROR_NB,NULL);
	}
	
	/// IS IT OK ?
	if (port_p->cmd_info.status==FINISHED)
	{
		atp_init_cmd_info_struct(port_p);
	}
	
	
	return RV_OK;
}




/******************************************************************************
* Function name: atp_init_cmd_info_struct
* Description : Initialise field of the icmd_info structure.
*
* Parameters :  port_p -> pointer on the port structure
*
* Return     :   return RV_OK
*
* History			: 0.1 (1-September-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_init_cmd_info_struct(T_ATP_PORT_STRUCT * port_p)
{
	// START SEMAPHORE ??
	port_p->cmd_info.cmd_txt_length=0;
	if (port_p->cmd_info.cmd_txt_p != NULL)
	{ 
		rvf_free_buf (port_p->cmd_info.cmd_txt_p);
	}
	port_p->cmd_info.cmd_txt_p = NULL;
	port_p->cmd_info.next_position=0;
	port_p->cmd_info.state = READY_FOR_NEW_CMD;
	port_p->cmd_info.status = NOT_STARTED;
	// STOP SEMAPHORE ??
	return RV_OK;
}



/******************************************************************************
* Function name: atp_error_switch
* Description : This function send ERROR events
*
* Parameters :  
*
* Return     :   return RV_OK
*				 or RV_MEMORY_ERR
*
* History			: 0.1 (1-September-2000) - Created
*
******************************************************************************/
T_ATP_RET atp_error_switch(T_ATP_ERROR_MAIN_REASON main_reason,
						   T_ATP_ERROR_TYPE error_type,T_RV_RETURN * return_path_p)
{
	T_ATP_ERROR * error_p;
	

	switch(main_reason)
	{
	case ATP_ERROR_FAILED_TO_OPEN_A_PORT:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to open a new port", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_ERROR_FAILED_TO_ACCEPT_A_PORT:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to accept a new port", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_ERROR_FAILED_TO_SEND_CMD:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to send a command on a port ", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_ERROR_FAILED_TO_CLOSE_A_PORT:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to close a port",RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_ERROR_FAILED_TO_SEND_DATA:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to send data", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_ERROR_FAILED_TO_GET_DATA:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to get data ", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_ERROR_FAILED_TO_HANDLE_MODE:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to handle mode related function", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_ERROR_FAILED_TO_HANDLE_SIGNAL:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to handle signal related function", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_ERROR_FAILED_TO_HANDLE_FLOW_REDIRECTION:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to redirect flow ", RV_TRACE_LEVEL_WARNING);
			break;
		}
	
	case ATP_ERROR_FAILED_TO_HANDLE_REGISTRATION:
		{	
			ATP_SEND_TRACE ("ATP :  Failed to register or deregister or get info on an other SWE ", RV_TRACE_LEVEL_WARNING);
			break;
		}
	default:
		{
			ATP_SEND_TRACE ("ATP :  Failed with an unkown main reason", RV_TRACE_LEVEL_WARNING);
			break;
		}
		
	}
	
	switch(error_type)
	{
	case ATP_MEMORY_ERROR:
		{
			ATP_SEND_TRACE ("ATP :  Memory Issue . ATP PRIM memory bank is RED !", RV_TRACE_LEVEL_ERROR);
			atp_error(ATP_ERROR_MB_PRIM_RED);			
			break;
		}
	case ATP_PARAM_ERROR:
		{
			ATP_SEND_TRACE ("ATP :  Function has been called with wrong parameter value(s) ", RV_TRACE_LEVEL_WARNING);
			break;
		}	
	case ATP_ISSUED_IN_A_WRONG_STATE_ERROR:
		{
			ATP_SEND_TRACE ("ATP :  Function has been called in a wrong state (port still not open or ATP not ready) ", RV_TRACE_LEVEL_WARNING);
			break;
		}	
	case ATP_WAITING_FOR_RESULT:
		{
			ATP_SEND_TRACE ("ATP :  Tried to send a new AT_CMD whereas the previous one did not ge any result yet", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_CANNOT_TRANSLATE_CMD:
		{
			ATP_SEND_TRACE ("ATP :  Failed to translate a command (interprete mode <-> text or data mode) ", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_OTHER_SWE_NOT_IN_PROPER_MODE:
		{
			ATP_SEND_TRACE ("ATP :  The other SWE is not in proper mode (COPY_ON/OFF, DCE ON/OFF ...)", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_SAME_ACTION_ALREADY_DONE:
		{
			ATP_SEND_TRACE ("ATP :  This action has already been performed earlier ", RV_TRACE_LEVEL_WARNING);
			break;
		}
	case ATP_NO_MORE_RESSOURCE:
		{
			ATP_SEND_TRACE ("ATP :  There is no more ressource to handle this action", RV_TRACE_LEVEL_WARNING);
			break;
		}
	default:
		{
			ATP_SEND_TRACE ("ATP :  Failed with an unkown error reason", RV_TRACE_LEVEL_WARNING);
			break;
		}
	}
	
	/* Get the primitive and sends it */
	if(return_path_p != NULL)
	{
		if (rvf_get_buf(atp_mb_prim,sizeof(T_ATP_ERROR),(void **) &error_p)==RVF_RED)
		{
			atp_error(ATP_ERROR_MB_PRIM_RED);
		}		
		
		error_p->rv_hdr.msg_id = ATP_ERROR;
		error_p->main_reason = main_reason;
		error_p->error_type = error_type;
		
		/* Send the event */
		return atp_send_message(*return_path_p,(T_ATP_MESSAGE *)error_p);
	}
	
	return RV_OK;				
}





/******************************************************************************
* Function name: atp_get_info_on_port_end
*
* Description : Provide information on the other end of the port 
* (for example, which format of data the other SW entity is expecting ) 
*
* Parameters :  see BT9901
*
* Return     :		RV_OK
*					RV_INVALID_PARAMETER : one of the id or port_nb was wrong : ignore call
*					RV_NOT_SUPPORTED : command needed to be translated and was unknow by ATP
*
*					atp_error can be called if MB is RED 
*
* History		: 0.1 19-Dec-2001
*				
******************************************************************************/
T_ATP_RET atp_get_info_on_port_end (T_ATP_SW_ENTITY_ID requester_sw_id, T_ATP_PORT_NB requester_port_nb,
					   T_ATP_OTHER_PORT_END_INFO * other_info_p)
{
	T_ATP_PORT_STRUCT * port_p;
	T_ATP_SW_NB requester_sw_nb, other_sw_nb;
	T_ATP_SW_ENTITY_STRUCT * other_sw_entity_p;
	
	/* Get the pointer on the port structure */
	if(atp_get_port(requester_sw_id,requester_port_nb,&port_p,&requester_sw_nb) != RV_OK) 
	{
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_PARAM_ERROR,NULL);
		return RV_INVALID_PARAMETER; /* This port does not exist */
	}
	other_sw_nb=(T_ATP_SW_NB) (! requester_sw_nb);
	
	if (port_p->port_state == ATP_OPEN_PENDING)
	{
		/* Port is not completely open yet */
		atp_error_switch(ATP_ERROR_FAILED_TO_SEND_CMD,ATP_ISSUED_IN_A_WRONG_STATE_ERROR,NULL);
		return RV_NOT_SUPPORTED;
	}
	
	/* get pointer on the other SW entity data structure in order to get info */
	other_sw_entity_p=atp_sw_entity_table_p[port_p->port_info[other_sw_nb].sw_id];

	/* Copy information */
	strcpy((char *) other_info_p->name,(char *) other_sw_entity_p->sw_entity_name);
	other_info_p->mode = other_sw_entity_p->mode;
	other_info_p->no_copy_info = (port_p->port_info[other_sw_nb]).no_copy_info;
	
return OK;
}



/******************************************************************************
* Function name: atp_mb_call_back
*
* Description : Indicate to a SW entity that the mb has ran GREEN again
*				 This function is called when a RX_MB which was RED 
*				(so TX_FLOW_OFF has been sent to the other SWE) 
*				, has been switched to GREEN . In this case, a TX_FLOW_ON has
*				to be sent to the SWE which was sending the data.
*
* Return     :		RV_OK if the signal has been sent to the proper SWE 
*				    Otherwise, RV_NOT_SUPPORTED
*
* History		: 0.1 27-05-2002
*				
******************************************************************************/
void atp_mb_call_back(T_RVF_MB_ID  mb)
{
	T_ATP_SW_NB        sw_nb   = ATP_MAX_SW_NB;
	T_ATP_PORT_STRUCT  *port_p = NULL;

	/* Searching for the ports whose RX memory bank matches with 'mb'. */
	for (port_p = atp_first_port_p;
		 port_p != NULL;
		 port_p = port_p->next_p)
	{

		/* Setting the flow control to ATP_RX_FLOW_ON. */
		if (((sw_nb = port_p->port_waiting_for_mb_callback) != ATP_MAX_SW_NB) && \
			((port_p->port_info[sw_nb]).rx_mb == mb))
		{

			/* Setting the sendee to an invalid value. */
			port_p->port_waiting_for_mb_callback = ATP_MAX_SW_NB;

			/* Setting the flow control to ATP_RX_FLOW_ON. */
			atp_set_signal ((port_p->port_info[sw_nb]).sw_id,
							(port_p->port_info[sw_nb]).port_nb,
							ATP_RX_FLOW_ON,
							ATP_RX_FLOW_UNMASK);
		}
	}
}