/*******************************************************************************
 *
 * UART.C
 *
 * This module allows to use the UARTs of chipset 1.5 in interrupt mode for
 * the Receive side and in polling mode for the Transmit side.
 * The driver calls a user's function when characters are received.
 *
 * (C) Texas Instruments 1999
 *
 ******************************************************************************/

#include "l1sw.cfg"
#include "chipset.cfg" 
#include "board.cfg"

#if (OP_L1_STANDALONE == 0)
  #include "main/sys_types.h"
#else
  #include "sys_types.h"
#endif

#include "uart/traceswitch.h"
#include "uart.h"

#include <string.h>

#include "memif/mem.h"

#if (BOARD != 34)
/*
 * Needed to reset and restart the sleep timer in case of incoming characters.
 */

#if (OP_L1_STANDALONE == 1)
  #include "serialswitch_core.h"
#else
  #include "uart/serialswitch.h" 
#endif

extern SYS_BOOL uart_sleep_timer_enabled;
#endif

#define BUFFER_SIZE (512) /* In bytes. */
#define FIFO_SIZE    (64) /* In bytes. */

#define STX                  0x02
#define DLE                  0x10

/*
 * TLR is used to program the RX FIFO trigger levels. FCR[7:4] are  not used.
 */
 
#define RX_FIFO_TRIGGER_LEVEL (12 << 4)


/*
 * 16750 addresses. Registers accessed when LCR[7] = 0.
 */

#define RHR (0x00) /* Rx buffer register - Read access   */
#define THR (0x00) /* Tx holding register - Write access */
#define IER (0x01) /* Interrupt enable register          */

/*
 * 16750 addresses. Registers accessed when LCR[7] = 1.
 */

#define DLL (0x00) /* Divisor latch (LSB) */
#define DLM (0x01) /* Divisor latch (MSB) */


/*
 * EFR is accessed when LCR[7:0] = 0xBF.
 */

#define EFR (0x02) /* Enhanced feature register */


/*
 * 16750 addresses. Bit 5 of the FCR register is accessed when LCR[7] = 1.
 */

#define IIR  (0x02) /* Interrupt ident. register - Read only */
#define FCR  (0x02) /* FIFO control register - Write only    */
#define LCR  (0x03) /* Line control register                 */
#define MCR  (0x04) /* Modem control register                */
#define LSR  (0x05) /* Line status register                  */
#define MSR  (0x06) /* Modem status register                 */
#define TCR  (0x06) /* Transmission control register         */
#define TLR  (0x07) /* Trigger level register                */
#define MDR1 (0x08) /* Mode definition register 1            */
#define SCR  (0x10) /* Supplementary Control register        */
#define SSR  (0x11) /* Supplementary Status register         */


/*
 * Supplementary control register.
 */

#define TX_EMPTY_CTL_IT (0x08)
#define RX_CTS_WAKE_UP_ENABLE_BIT (4) /* Use RESET_BIT and SET_BIT macros. */

/*
 * Enhanced feature register.
 */
 
#define ENHANCED_FEATURE_BIT (4) /* Use RESET_BIT and SET_BIT macros. */

/*
 * Mode definition register 1.
 */

#define UART_MODE             (0x00)
#define SIR_MODE              (0x01)
#define UART_MODE_AUTOBAUDING (0x02) /* Reserved in UART/IrDA. */
#define RESET_DEFAULT_STATE   (0x07)
#define IR_SLEEP_DISABLED     (0x00)
#define IR_SLEEP_ENABLED      (0x08)
#define SIR_TX_WITHOUT_ACREG2 (0x00) /* Reserved in UART/modem. */
#define SIR_TX_WITH_ACREG2    (0x20) /* Reserved in UART/modem. */
#define FRAME_LENGTH_METHOD   (0x00) /* Reserved in UART/modem. */
#define EOT_BIT_METHOD        (0x80) /* Reserved in UART/modem. */

/*
 * Supplementary Status Register
 */

#define TX_FIFO_FULL (0x01)


/*
 * Interrupt enable register.
 */

#define ERBI  (0x01) /* Enable received data available interrupt            */
#define ETBEI (0x02) /* Enable transmitter holding register empty interrupt */
#define ELSI  (0x04) /* Enable receiver line status interrupt               */
#define EDSSI (0x08) /* Enable modem status interrupt                       */
#define IER_SLEEP (0x10)  /* Enable sleep mode                              */

/*
 * Modem control register.
 */

#define MDTR (0x01) /* Data terminal ready. */
#define MRTS (0x02) /* Request to send.     */
#define TCR_TLR_BIT (6)

/*
 * Line status register.
 */

#define DR   (0x01) /* Data ready                                  */
#define OE   (0x02) /* Overrun error                               */
#define PE   (0x04) /* Parity error                                */
#define FE   (0x08) /* Framing error                               */
#define BI   (0x10) /* Break interrupt                             */
#define THRE (0x20) /* Transmitter holding register (FIFO empty)   */
#define TEMT (0x40) /* Transmitter empty (FIFO and TSR both empty) */

/*
 * Interrupt identification register.
 * Bit 0 is set to 0 if an IT is pending.
 * Bits 1 and 2 are used to identify the IT.
 */

#define IIR_BITS_USED  (0x07)
#define IT_NOT_PENDING (0x01)
#define RX_DATA        (0x04)
#define TX_EMPTY       (0x02)
#define MODEM_STATUS   (0x00)

/*
 * Line control register.
 */

#define WLS_5         (0x00) /* Word length: 5 bits                    */
#define WLS_6         (0x01) /* Word length: 6 bits                    */
#define WLS_7         (0x02) /* Word length: 7 bits                    */
#define WLS_8         (0x03) /* Word length: 8 bits                    */
#define STB           (0x04) /* Number of stop bits: 0: 1, 1: 1,5 or 2 */
#define PEN           (0x08) /* Parity enable                          */
#define EPS           (0x10) /* Even parity select                     */
#define BREAK_CONTROL (0x40) /* Enable a break condition               */
#define DLAB          (0x80) /* Divisor latch access bit               */
#define DIV_EN_BIT    (7)

/*
 * FIFO control register.
 */

#define FIFO_ENABLE   (0x01)
#define RX_FIFO_RESET (0x02)
#define TX_FIFO_RESET (0x04)

/*
 * These macros allow to read and write a UART register.
 */

#define READ_UART_REGISTER(UART,REG) \
            *((volatile SYS_UWORD8 *) ((UART)->base_address + (REG)))

#define WRITE_UART_REGISTER(UART,REG,VALUE) \
            *((volatile SYS_UWORD8 *) ((UART)->base_address + (REG))) = (VALUE)

#define RESET_BIT(UART,REG,BIT)    \
			(WRITE_UART_REGISTER ( \
			     UART, REG, READ_UART_REGISTER (UART, REG) & ~(1 << (BIT))))

#define SET_BIT(UART,REG,BIT)      \
			(WRITE_UART_REGISTER ( \
			     UART, REG, READ_UART_REGISTER (UART, REG) | (1 << (BIT))))
            
/* 
 * These macros allow to enable or disable the wake-up interrupt.
 */

#define ENABLE_WAKEUP_INTERRUPT(UART)   \
	SET_BIT(UART, SCR, RX_CTS_WAKE_UP_ENABLE_BIT);

#define DISABLE_WAKEUP_INTERRUPT(UART)   \
	RESET_BIT(UART, SCR, RX_CTS_WAKE_UP_ENABLE_BIT);


/* 
 * This macro allows to know if the RX buffer is full. It must be called only
 * from the RX interrupt handler. If it is called from the application, the
 * rx_in pointer may be updated if a RX interrupt occurs.
 */

#define RX_BUFFER_FULL(UART)                                      \
            (((UART)->rx_in == (UART)->rx_out - 1) ||             \
             ((UART)->rx_in == (UART)->rx_out + BUFFER_SIZE - 1))


/* 
 * This allows monitor the last 32 inbound buffers gotten from the RX FIFO.
 */

//#define UART_RX_BUFFER_DUMP

#ifdef UART_RX_BUFFER_DUMP
struct {
    char rx_buffer[(BUFFER_SIZE + 1) << 5];
    char *rx_in;
    int  errors_count;
    int  wrong_interrupt_status;
} uart_rx_buffer_dump = {0};
#endif


typedef struct s_uart {

    SYS_UWORD32 base_address;

    /*
     * Buffers management.
     */
    
    char rx_buffer[BUFFER_SIZE + 1];
    char *rx_in;
    char *rx_out;
    void (*callback_function) (void);

    /*
     * Errors counters.
     */

    SYS_UWORD32 framing_error;
    SYS_UWORD32 parity_error;
    SYS_UWORD32 overrun_error;

    /*
     * Framing flags.
     */

    SYS_BOOL dle_detected;
    SYS_BOOL inframe;
    SYS_BOOL encapsulation_flag;
    unsigned char frame_length;

} t_uart;

static t_uart uart_parameter[NUMBER_OF_TR_UART];

static const SYS_UWORD32 base_address[NUMBER_OF_TR_UART] =
{
    MEM_UART_IRDA,
    MEM_UART_MODEM
    #if (CHIPSET == 12)
      , MEM_UART_MODEM2
    #endif
};


/*
 * DLL (LSB) and DLH (MSB) registers values using the 13 MHz clock.
 */

static const SYS_UWORD8 dll[] =
{
      2, /* 406250 baud. */
      7, /* 115200 baud. */
     14, /*  57600 baud. */
     21, /*  38400 baud. */
     24, /*  33900 baud. */
     28, /*  28800 baud. */
     42, /*  19200 baud. */
     56, /*  14400 baud. */
     84, /*   9600 baud. */
    169, /*   4800 baud. */
     83, /*   2400 baud. */
    165, /*   1200 baud. */
     74, /*    600 baud. */
    148, /*    300 baud. */
     40, /*    150 baud. */
     81  /*     75 baud. */
};

static const SYS_UWORD8 dlh[] =
{
     0, /* 406250 baud. */
     0, /* 115200 baud. */
     0, /*  57600 baud. */
     0, /*  38400 baud. */
     0, /*  33900 baud. */
     0, /*  28800 baud. */
     0, /*  19200 baud. */
     0, /*  14400 baud. */
     0, /*   9600 baud. */
     0, /*   4800 baud. */
     1, /*   2400 baud. */
     2, /*   1200 baud. */
     5, /*    600 baud. */
    10, /*    300 baud. */
    21, /*    150 baud. */
    42  /*     75 baud. */
};


/*******************************************************************************
 *
 *                              read_rx_fifo
 * 
 * Purpose  : Check the bytes written into the RX FIFO. Characters are not
 *            written in the RX buffer if it is full. The HISR is called if
 *            enough characters are received.
 *
 * Arguments: In : uart: pointer on UART structure.
 *            Out: none
 *
 * Returns  : none
 *
 ******************************************************************************/

static void
read_rx_fifo (t_uart *uart)
{
    volatile SYS_UWORD8 status;
    int                 error_detected;
    SYS_UWORD8          char_received;

#if (BOARD != 34)
    /*
     * Since new characters have been received, the sleep timer is reset then
     * restarted preventing the system to enter deep-sleep for a new period of
     * time.
     */

    SER_activate_timer_hisr ();
    uart_sleep_timer_enabled = 1;
#endif
    
    status = READ_UART_REGISTER (uart, LSR);

    while (status & DR) { /* While RX FIFO is not empty... */

        error_detected = 0;

        char_received = READ_UART_REGISTER (uart, RHR);

        /*
         * Check if an error (overrun, parity, framing or break) is associated with the
         * received data. If there is an error the byte is not copied into the
         * RX buffer.
         */

        if (status & (OE | PE | FE | BI)) {

            if (status & PE)
                uart->parity_error++;

            if (status & FE)
                uart->framing_error++;

            if (status & OE)
                uart->overrun_error++;

            error_detected = 1;
        }

        /*
         * If there is no error the byte is copied into the RX
         * buffer if it is not full.
         */

        if (!error_detected && !RX_BUFFER_FULL (uart)) {

            *(uart->rx_in++) = char_received;

            if (uart->rx_in == &(uart->rx_buffer[0]) + BUFFER_SIZE + 1)
                uart->rx_in = &(uart->rx_buffer[0]);

#ifdef UART_RX_BUFFER_DUMP
            *(uart_rx_buffer_dump.rx_in)++ = char_received;

            if (uart_rx_buffer_dump.rx_in == uart_rx_buffer_dump.rx_buffer + sizeof (uart_rx_buffer_dump.rx_buffer))
                uart_rx_buffer_dump.rx_in = uart_rx_buffer_dump.rx_buffer;
        }
        else {
            uart_rx_buffer_dump.errors_count++;
#endif
        }

        status = READ_UART_REGISTER (uart, LSR);
    }

    /*
     * Call the user's function.
     */

    if (uart->callback_function != NULL)
        (*(uart->callback_function)) ();
}

/*******************************************************************************
 *
 *                           initialize_uart_sleep
 * 
 * Purpose  : Performs basic UART hardware initialization including sleep mode.
 *
 * Arguments: In : uart_id : UART id.
 *            Out: none
 *
 * Returns: none
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

void
initialize_uart_sleep (T_tr_UartId uart_id)
{
    t_uart     *uart;
    int        index;
    SYS_UWORD8 dummy;

    for (index = 0; index < NUMBER_OF_TR_UART; index++)
        uart_parameter[index].base_address = base_address[index];
        
    uart = &(uart_parameter[uart_id]);

    /*
     * Mask all interrupts causes and disable sleep mode.
     */

    WRITE_UART_REGISTER (uart, IER, 0x00);

    /*
     * Reset UART mode configuration.
     */
     
    WRITE_UART_REGISTER (uart, MDR1, RESET_DEFAULT_STATE);

    /*
     * LCR[7:0] = 0xBF to allow to access EFR 
     * EFR[4] = 1 to allow to program IER[4].
     */
     
    WRITE_UART_REGISTER (uart, LCR, 0xBF);
    SET_BIT (uart, EFR, ENHANCED_FEATURE_BIT);
    WRITE_UART_REGISTER (uart, LCR, 0x83);

    /*
     * Enable FIFO and reset them.
     */

    WRITE_UART_REGISTER (uart, FCR, FIFO_ENABLE   |
                                    RX_FIFO_RESET |
                                    TX_FIFO_RESET);

    /*
     * Program the baud generator (dummy 115200).
     */

    WRITE_UART_REGISTER (uart, DLL, 0x07);
    WRITE_UART_REGISTER (uart, DLM, 0x00);

    /*
     * LCR[7] = 0 to allow to access IER and RHR - normal mode.
     */
     
    RESET_BIT (uart, LCR, DIV_EN_BIT);

    /*
     * Select UART mode.
     */
     
    WRITE_UART_REGISTER (uart, MDR1, UART_MODE);

    /*
     * Clear Interrupt and check that Rx FIFO is empty.
     */

    dummy = READ_UART_REGISTER (uart, IIR);

    while (READ_UART_REGISTER (uart, LSR) & DR)
	    dummy = READ_UART_REGISTER (uart, RHR);

#if ((CHIPSET != 5) && (CHIPSET != 6))
    /*
     * Enable sleep mode.
     */

    WRITE_UART_REGISTER (uart, IER, IER_SLEEP);
#endif
}


/*******************************************************************************
 *
 *                               UA_Init
 * 
 * Purpose  : Initializes the module and the UART.
 *
 * Arguments: In : uart_id : UART id.
 *                 baudrate: baud rate selected.
 *                 callback: user's function called characters are received.
 *            Out: none
 *
 * Returns: none
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

void
UA_Init (T_tr_UartId uart_id,
         T_tr_Baudrate baudrate,
         void (callback_function (void)))
{
    t_uart *uart;
    int    index;

#ifdef UART_RX_BUFFER_DUMP
    uart_rx_buffer_dump.rx_in = uart_rx_buffer_dump.rx_buffer;
#endif

    for (index = 0; index < NUMBER_OF_TR_UART; index++)
        uart_parameter[index].base_address = base_address[index];
        
    uart = &(uart_parameter[uart_id]);

    uart->rx_in  = &(uart->rx_buffer[0]);
    uart->rx_out = &(uart->rx_buffer[0]);

    uart->callback_function = callback_function;

    uart->framing_error = 0;
    uart->parity_error  = 0;
    uart->overrun_error = 0;

    uart->dle_detected = 0;
    uart->inframe = 0;
    uart->encapsulation_flag = 0;
    uart->frame_length = 0;

    /*
     * Mask all interrupts causes and disable sleep mode.
     */

    WRITE_UART_REGISTER (uart, IER, 0x00);

    /*
     * Reset UART mode configuration.
     */
     
    WRITE_UART_REGISTER (uart, MDR1, RESET_DEFAULT_STATE   |
                                     IR_SLEEP_DISABLED     |
                                     SIR_TX_WITHOUT_ACREG2 |
                                     FRAME_LENGTH_METHOD);

    /*
     * FIFO configuration.
     * EFR[4] = 1 to allow to program FCR[5:4] and MCR[7:5].
     */
     
    WRITE_UART_REGISTER (uart, LCR, 0xBF);
    SET_BIT (uart, EFR, ENHANCED_FEATURE_BIT);

    /*
     * Select the word length, the number of stop bits , the parity and set
     * LCR[7] (DLAB) to allow to program FCR, DLL and DLM.
     */

    WRITE_UART_REGISTER (uart, LCR, WLS_8 | DLAB);

    /*
     * Program the trigger levels.
     * MCR[6] must be set to 1.
     */
     
    SET_BIT (uart, MCR, TCR_TLR_BIT);
    WRITE_UART_REGISTER (uart, TCR, 0x0F);
    WRITE_UART_REGISTER (
        uart, TLR, RX_FIFO_TRIGGER_LEVEL);
    
    /*
     * Program the FIFO control register. Bit 0 must be set when other FCR bits
     * are written to or they are not programmed.
     * FCR is a write-only register. It will not be modified.
     */

    WRITE_UART_REGISTER (uart, FCR, FIFO_ENABLE   |
                                    RX_FIFO_RESET | /* self cleared */
                                    TX_FIFO_RESET); /* self cleared */

    /*
     * Program the baud generator.
     */

    WRITE_UART_REGISTER (uart, DLL, dll[baudrate]);
    WRITE_UART_REGISTER (uart, DLM, dlh[baudrate]);

    
    /*
     * Reset LCR[7] (DLAB) to have access to the RBR, THR and IER registers.
     */

    WRITE_UART_REGISTER (uart, LCR, READ_UART_REGISTER (uart, LCR) & ~DLAB);


    /*
     * Select UART mode.
     */
     
    WRITE_UART_REGISTER (uart, MDR1, UART_MODE             |
                                     IR_SLEEP_DISABLED     |
                                     SIR_TX_WITHOUT_ACREG2 |
                                     FRAME_LENGTH_METHOD);

#if ((CHIPSET == 5) || (CHIPSET == 6))
    /*
     * Unmask RX interrupt
     */

    WRITE_UART_REGISTER (uart, IER, ERBI);
#else
    /*
     * Unmask RX interrupt and allow sleep mode.
     */

    WRITE_UART_REGISTER (uart, IER, ERBI | IER_SLEEP);
#endif
}

/*******************************************************************************
 *
 *                           UA_ReadNChars
 * 
 * Purpose  : Reads N characters from the RX buffer.
 *
 * Arguments: In : uart_id      : UART id.
 *                 buffer       : buffer address where the characters are
 *                                copied.
 *                 chars_to_read: number of characters to read.
 *            Out: none
 *
 * Returns  : The number of characters read.
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

SYS_UWORD32
UA_ReadNChars (T_tr_UartId uart_id,
               char *buffer,
               SYS_UWORD32 chars_to_read)
{
    SYS_UWORD32 chars_in_rx_buffer;
    SYS_UWORD32 chars_to_copy;
    SYS_UWORD32 chars_written;
    char        *rx_in;
    t_uart      *uart;
    
    uart = &(uart_parameter[uart_id]);

    /*
     * A copy of the rx_in pointer is used because it may be updated by
     * the interrupt handler.
     * Get the number of bytes available in the RX buffer.
     */
         
    rx_in = uart->rx_in;

    if (uart->rx_out <= rx_in)
        chars_in_rx_buffer = (SYS_UWORD32) (rx_in - uart->rx_out);
    else
        chars_in_rx_buffer = (SYS_UWORD32) (rx_in - uart->rx_out + BUFFER_SIZE + 1);

    /*
     * No more bytes than those received may be written in the output buffer.
     */

    if (chars_in_rx_buffer >= chars_to_read)
        chars_to_copy = chars_to_read;
    else
        chars_to_copy = chars_in_rx_buffer;

    chars_written = chars_to_copy;

    /*
     * Write the received bytes in the output buffer.
     */

    while (chars_to_copy) {

        *(buffer++) = *(uart->rx_out++);
        chars_to_copy--;

        if (uart->rx_out == &(uart->rx_buffer[0]) + BUFFER_SIZE + 1)
            uart->rx_out = &(uart->rx_buffer[0]);
    }

    return (chars_written);
}

/*******************************************************************************
 *
 *                           UA_ReadNBytes
 * 
 * Purpose  : Reads and destuff N bytes from the RX buffer.
 *
 * Arguments: In : uart_id      : UART id.
 *                 buffer       : buffer address where the bytes are copied.
 *                 chars_to_read: number of bytes to read.
 *            Out: eof_detected : indicates if an EOF has been detected. Possible
 *                                values are:
 *                                 - 0: EOF not detected,
 *                                 - 1: EOF detected and no more bytes left,
 *                                 - 2: EOF not detected and more bytes left.
 *                                      Users must invoke this function one more
 *                                      time in order to get those remaining
 *                                      bytes,
 *                                 - 3: EOF detected and more bytes left. Users
 *                                      must invoke this function one more time
 *                                      in order to get those remaining bytes.
 *
 * Returns  : The number of bytes read.
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

SYS_UWORD32
UA_ReadNBytes (T_tr_UartId uart_id,
               char *buffer_p,
               SYS_UWORD32 bytes_to_read,
               SYS_BOOL *eof_detected_p)
{
    SYS_UWORD32 bytes_written;
    SYS_UWORD32 bytes_in_rx_buffer;
    SYS_UWORD32 bytes_to_process;
    t_uart      *uart_p;
    char        *rx_in_p;

    bytes_written = 0;
    uart_p = &(uart_parameter[uart_id]);

    /*
     * A copy of the rx_in pointer is used because it may be updated by
     * the interrupt handler.
     * Get the number of bytes available in the RX buffer.
    */

    rx_in_p = uart_p->rx_in;

    if (uart_p->rx_out <= rx_in_p)
        bytes_in_rx_buffer = (SYS_UWORD32) (rx_in_p - uart_p->rx_out);
    else
        bytes_in_rx_buffer = (SYS_UWORD32) (rx_in_p - uart_p->rx_out + BUFFER_SIZE + 1);

    /*
     * No more bytes than those received may be processed and then written
     * in the output buffer.
     */

    if (bytes_in_rx_buffer > bytes_to_read) {
        bytes_to_process = bytes_to_read;

        /*
         * More bytes left. Users must invoke this function one more time
		 * in order to get those remaining bytes.
		 */

        *eof_detected_p  = 2;
    }
    else {
        bytes_to_process = bytes_in_rx_buffer;

        /*
         * No more bytes left.
		 */

        *eof_detected_p  = 0;
    }

    /*
     * Perform the byte destuffing and then write the "valid" received bytes in
     * the output buffer.
     */

	while ((bytes_to_process) && !(*eof_detected_p & 0x01)) {

        switch (*(uart_p->rx_out)) {

            /*
             * Current byte is DLE.
             */

            case DLE:
                                                        
                if (!uart_p->dle_detected) {

                    /*
                     * No DLE previously detected => 
                     * Skip the current byte and set the flag.
                     */

                    uart_p->dle_detected = 1;
                    uart_p->rx_out++;
                }

                else { /* if (uart_p->dle_detected) */

                    if (uart_p->inframe) {

                        /*
                         * DLE previously detected AND currently inside of a frame =>
                         * Copy the current byte in the output buffer, reset the flag
                         * and increase the frame length.
                         */

                        uart_p->dle_detected = 0;
                        uart_p->frame_length++;
                        *(buffer_p++) = *(uart_p->rx_out++);
                        bytes_written++;
                    }

                    else { /* if (!uart_p->inframe) */

                        /*
                         * DLE previously detected AND currently outside of a frame =>
                         * Skip the current byte.
                         */

                        uart_p->rx_out++;
                    }
                }

            break; /* case DLE */

            /*
             * Current byte is STX.
             */

            case STX:
                                                      
                if ((!uart_p->dle_detected) && (uart_p->inframe)) {

                    /*
                     * No DLE previously detected AND currently inside of a frame.
                     */

                    if (uart_p->frame_length) {

                        /*
                         * Frame length is not zero (End of Frame) => 
                         * Skip the current byte and set the flags (EOF).
                         */

                        uart_p->inframe = 0;
                        uart_p->frame_length = 0;
                        uart_p->rx_out++;

                        /*
                         * More bytes left.
                         */

                        if ((*eof_detected_p == 0) && (bytes_to_process))
                            *eof_detected_p = 2;

                        /*
                         * EOF detected.
                         */

                        (*eof_detected_p)++;
                    }

                    else { /* if (!uart_p->frame_length) */

                        /*
                         * Frame length is zero (STX followed by another STX =
                         * Synchro lost but start of a new frame) =>
                         * Skip the current byte and keep the flag set.
                         */

                        uart_p->rx_out++;
                    }
                }

                else if ((!uart_p->dle_detected) && (!uart_p->inframe)) {

                    /*
                     * No DLE previously detected AND currently outside of a
                     * frame (Start of Frame) =>
                     * Skip the current byte and set the flag.
                     */

                    uart_p->inframe = 1;
                    uart_p->rx_out++;
                }

                else if ((uart_p->dle_detected) && (uart_p->inframe)) {

                    /*
                     * DLE previously detected AND currently inside of a frame =>
                     * Copy the current byte in the output buffer, reset the flag
                     * and increase the frame length.
                     */

                    uart_p->dle_detected = 0;
                    uart_p->frame_length++;
                    *(buffer_p++) = *(uart_p->rx_out++);
                    bytes_written++;
                }

                else if ((uart_p->dle_detected) && (!uart_p->inframe)) {

                    /*
                     * DLE previously detected AND currently outside of a frame =>
                     * Skip the current byte and reset the flag.
                     */

                    uart_p->dle_detected = 0;
                    uart_p->rx_out++;
                }

            break; /* case STX */

            /*
             * Current byte is neither DLE nor STX.
             */

            default:

                if (uart_p->inframe) {

                    /*
                     * Currently inside of a frame =>
                     * Copy the current byte in the output buffer and increase
                     * the frame length.
                     */

                    uart_p->frame_length++;
                    *(buffer_p++) = *(uart_p->rx_out++);
                    bytes_written++;
                }

                else { /* if (!uart_p->inframe) */

                    /*
                     * Currently outside of a frame =>
                     * Skip the current byte.
                     */

                    uart_p->rx_out++;
                }

            break; /* default */
        }

        if (uart_p->rx_out == &(uart_p->rx_buffer[0]) + BUFFER_SIZE + 1)
            uart_p->rx_out = &(uart_p->rx_buffer[0]);

        bytes_to_process--;
    }

    return (bytes_written);
}


/*******************************************************************************
 *
 *                           UA_WriteNChars
 * 
 * Purpose  : Writes N characters in the TX FIFO.
 *
 * Arguments: In : uart_id       : UART id.
 *                 buffer        : buffer address from which characters are
 *                                 written.
 *                 bytes_to_write: number of bytes to write.
 *            Out: none
 *
 * Returns  : Number of bytes written.
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

SYS_UWORD32
UA_WriteNChars (T_tr_UartId uart_id,
                char *buffer,
                SYS_UWORD32 chars_to_write)
{
    SYS_UWORD32 chars_in_tx_fifo;
    SYS_UWORD32 chars_written;
    t_uart      *uart;
    
    chars_written = 0;
    uart = &(uart_parameter[uart_id]);

#if ((CHIPSET != 5) && (CHIPSET != 6))
    /*
     * Disable sleep mode.
     */
              
    WRITE_UART_REGISTER (
        uart, IER, READ_UART_REGISTER (uart, IER) & ~IER_SLEEP);
#endif

    /*
     * Copy the input buffer to the TX FIFO.
     * Ulyssse Bug #44: TX FIFO full status bit (SSR[1]) is corrupted during
     * one period of Bclock => Workaround S/W.
     * Write in TX FIFO only if FIFO is empty instead of writing in TX FIFO
     * while FIFO is not full.
     */

    if (READ_UART_REGISTER (uart, LSR) & THRE) {

        chars_in_tx_fifo = 0;

        while ((chars_written < chars_to_write) &&
		       (chars_in_tx_fifo < FIFO_SIZE)) {

            WRITE_UART_REGISTER (uart, THR, *(buffer++));
            chars_written++;
            chars_in_tx_fifo++;
        }
    }

#if ((CHIPSET != 5) && (CHIPSET != 6))
    /*
     * Re-enable sleep mode.
     */

    WRITE_UART_REGISTER ( 
        uart, IER, READ_UART_REGISTER (uart, IER) | IER_SLEEP);
#endif

    return (chars_written);
}


/*******************************************************************************
 *
 *                           UA_EncapsulateNChars
 * 
 * Purpose  : Writes N characters in the TX FIFO in encapsulating them with 2
 *            STX bytes (one at the beginning and one at the end).
 *
 * Arguments: In : uart_id       : UART id.
 *                 buffer        : buffer address from which characters are
 *                                 written.
 *                 chars_to_write: number of chars to write.
 *            Out: none
 *
 * Returns  : Number of chars written.
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

SYS_UWORD32
UA_EncapsulateNChars (T_tr_UartId uart_id,
                      char *buffer,
                      SYS_UWORD32 chars_to_write)
{
    SYS_UWORD32 chars_written;
    SYS_UWORD32 chars_in_tx_fifo;
    t_uart      *uart;
    
    chars_written = 0;
    uart = &(uart_parameter[uart_id]);

#if ((CHIPSET != 5) && (CHIPSET != 6))
    /*
     * Disable sleep mode.
     */
              
    WRITE_UART_REGISTER (
        uart, IER, READ_UART_REGISTER (uart, IER) & ~IER_SLEEP);
#endif

    /*
     * Copy the input buffer to the TX FIFO.
     * Ulyssse Bug #44: TX FIFO full status bit (SSR[1]) is corrupted during
     * one period of Bclock => Workaround S/W.
     * Write in TX FIFO only if FIFO is empty instead of writing in TX FIFO
     * while FIFO is not full.
     */

    if (READ_UART_REGISTER (uart, LSR) & THRE) {

        chars_in_tx_fifo = 0;

        /*
         * Check if the message has been already encapsulated.
         */

        if (!uart->encapsulation_flag) {
            /*
             * Write STX in the TX FIFO and set the flag.
             */

            WRITE_UART_REGISTER (uart, THR, STX);
            chars_in_tx_fifo++;
            uart->encapsulation_flag = 1;
        }

        /*
         * Keep one char margin in the TX FIFO for the last STX.
         */

        while ((chars_written < chars_to_write) &&
               (chars_in_tx_fifo < (FIFO_SIZE-1))) {

            WRITE_UART_REGISTER (uart, THR, *(buffer++));
            chars_written++;
            chars_in_tx_fifo++;
        }

        /*
         * Append STX byte at the end if the frame is complete.
         */

        if (chars_written == chars_to_write) {

            /*
             * Write STX in the TX FIFO and reset the flag.
             */

            WRITE_UART_REGISTER (uart, THR, STX);
            uart->encapsulation_flag = 0;
        }
    }

#if ((CHIPSET != 5) && (CHIPSET != 6))
    /*
     * Re-enable sleep mode.
     */

    WRITE_UART_REGISTER ( 
        uart, IER, READ_UART_REGISTER (uart, IER) | IER_SLEEP);
#endif

    return (chars_written);
}


/*******************************************************************************
 *
 *                           UA_WriteNBytes
 * 
 * Purpose  : Writes N bytes in the TX FIFO in encapsulating with 2 STX bytes
 *            at the beginning and the end of the frame, and in making byte
 *            stuffing.
 *
 * Arguments: In : uart_id       : UART id.
 *                 buffer        : buffer address from which bytes are
 *                                 written.
 *                 bytes_to_write: number of bytes to write.
 *            Out: none
 *
 * Returns  : Number of bytes written.
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

SYS_UWORD32
UA_WriteNBytes (T_tr_UartId uart_id,
                SYS_UWORD8 *buffer,
                SYS_UWORD32 bytes_to_write)
{
    SYS_UWORD32 bytes_written;
    SYS_UWORD32 bytes_in_tx_fifo;
    t_uart      *uart;
        
    bytes_written = 0;
    uart = &(uart_parameter[uart_id]);

#if ((CHIPSET != 5) && (CHIPSET != 6))
    /*
     * Disable sleep mode.
     */
              
    WRITE_UART_REGISTER (
        uart, IER, READ_UART_REGISTER (uart, IER) & ~IER_SLEEP);
#endif

    /*
     * Copy the input buffer to the TX FIFO.
     * Ulyssse Bug #44: TX FIFO full status bit (SSR[1]) is corrupted during
     * one period of Bclock => Workaround S/W.
     * Write in TX FIFO only if FIFO is empty instead of writing in TX FIFO
     * while FIFO is not full.
     */

    if (READ_UART_REGISTER (uart, LSR) & THRE) {

        bytes_in_tx_fifo = 0;

        /*
         * Check if the message has been already encapsulated.
         */

        if (!uart->encapsulation_flag) {

            /*
             * Write STX in the TX FIFO and set the flag.
             */

            WRITE_UART_REGISTER (uart, THR, STX);
            bytes_in_tx_fifo++;
            uart->encapsulation_flag = 1;
        }

        /*
         * Keep 2 chars margin in the FIFO, one for the stuffing (if necessary)
         * and one for the last STX.
         */

        while ((bytes_written < bytes_to_write) && 
               (bytes_in_tx_fifo < (FIFO_SIZE-2))) {

            /*
             * Check for STX or DLE in order to perform the stuffing.
             */

            if ((*(buffer) == STX) || (*(buffer) == DLE)) {

                /*
                 * Write DLE in the TX FIFO.
                 */

                WRITE_UART_REGISTER (uart, THR, DLE);
                bytes_in_tx_fifo++;
            }

            WRITE_UART_REGISTER (uart, THR, *(buffer++));
            bytes_written++;
            bytes_in_tx_fifo++;
        }

        /*
         * Append STX byte at the end if the frame is complete.
         */

        if (bytes_written == bytes_to_write) {

            /*
             * Write STX in the TX FIFO and reset the flag.
             */

            WRITE_UART_REGISTER (uart, THR, STX);
            uart->encapsulation_flag = 0;
        }
    }

#if ((CHIPSET != 5) && (CHIPSET != 6))
    /*
     * Re-enable sleep mode.
     */

    WRITE_UART_REGISTER ( 
        uart, IER, READ_UART_REGISTER (uart, IER) | IER_SLEEP);
#endif

    return (bytes_written);
}


/*******************************************************************************
 *
 *                            UA_WriteChar
 * 
 * Purpose  : Writes a character in the TX FIFO.
 *
 * Arguments: In : uart: UART id.
 *                 character
 *            Out: none
 *
 * Returns  : none
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

void
UA_WriteChar (T_tr_UartId uart_id,
              char character)
{
    (void) UA_WriteNChars (uart_id, &character, 1);
}

/*******************************************************************************
 *
 *                          UA_WriteString
 * 
 * Purpose  : Writes a null terminated string in the TX FIFO.
 *
 * Arguments: In : uart_id: UART id.
 *                 buffer : buffer address from which characters are written.
 *            Out: none
 *
 * Returns  : none
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

void
UA_WriteString (T_tr_UartId uart_id,
                char *buffer)
{
    (void) UA_WriteNChars (uart_id, buffer, strlen (buffer));
}

/*******************************************************************************
 *
 *                             UA_EnterSleep
 * 
 * Purpose  : Checks if UART is ready to enter Deep Sleep. If ready, enables
 *            wake-up interrupt.
 *
 * Arguments: In : uart_id : UART id.
 *            Out: none
 *
 * Returns: 0	 : Deep Sleep is not possible.
 *          >= 1 : Deep Sleep is possible.
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

SYS_BOOL
UA_EnterSleep (T_tr_UartId uart_id)
{
    t_uart              *uart;
    SYS_BOOL            deep_sleep;
    volatile SYS_UWORD8 status;
        
    uart = &(uart_parameter[uart_id]);
    deep_sleep = 0;

    /*
     * Check if RX & TX FIFOs are both empty
     */

    status = READ_UART_REGISTER (uart, LSR);

    if (!(status & DR) &&
        (status & TEMT)) {

#if ((CHIPSET != 5) && (CHIPSET != 6))
        /*
         * Disable sleep mode.
         */
              
        WRITE_UART_REGISTER (
            uart, IER, READ_UART_REGISTER (uart, IER) & ~IER_SLEEP);
#endif

        /*
         * Mask RX interrupt.
         */

        WRITE_UART_REGISTER (
            uart, IER, READ_UART_REGISTER (uart, IER) & ~ERBI);

        /*
         * Enable the wake-up interrupt.
         */

        ENABLE_WAKEUP_INTERRUPT (uart);

        deep_sleep = 1;
    }

    return (deep_sleep);
}

/*******************************************************************************
 *
 *                              UA_WakeUp
 * 
 * Purpose  : Wakes up UART after Deep Sleep.
 *
 * Arguments: In : uart_id : UART id.
 *            Out: none
 *
 * Returns: none
 *
 * Warning: Parameters are not verified.
 *
 ******************************************************************************/

void
UA_WakeUp (T_tr_UartId uart_id)
{
    t_uart *uart;
       
    uart = &(uart_parameter[uart_id]);

    /*
     * Disable the wake-up interrupt.
     */

    DISABLE_WAKEUP_INTERRUPT (uart);

    /*
     * Unmask RX interrupts.
     */

    WRITE_UART_REGISTER (
        uart, IER, READ_UART_REGISTER (uart, IER) | ERBI);

#if ((CHIPSET != 5) && (CHIPSET != 6))
    /*
     * Allow sleep mode.
     */

    WRITE_UART_REGISTER ( 
        uart, IER, READ_UART_REGISTER (uart, IER) | IER_SLEEP);
#endif
}

/*******************************************************************************
 *
 *                       UA_InterruptHandler
 * 
 * Purpose  : Interrupt handler.
 *
 * Arguments: In : uart_id         : origin of interrupt
 *                 interrupt_status: source of interrupt
 *            Out: none
 *
 * Returns  : none
 *
 ******************************************************************************/

void
UA_InterruptHandler (T_tr_UartId uart_id,
                     SYS_UWORD8 interrupt_status)
{
    t_uart *uart;
   
    uart = &(uart_parameter[uart_id]);

    switch (interrupt_status) {

        case RX_DATA:

            read_rx_fifo (uart);

        break;

        default:

#ifdef UART_RX_BUFFER_DUMP
            uart_rx_buffer_dump.wrong_interrupt_status++;
#endif

            /* No Processing */

        break;
    }
}
