view src/cs/drivers/drv_core/uart/uart.c @ 303:f76436d19a7a default tip

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

/*******************************************************************************
 *
 * 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. */
      1  /* 812500 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. */
     0  /* 812500 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;
    }
}