/*******************************************************************************
*
* 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);
		}
	}
}
