//==========================================================================
//
//      devs/serial/arm/str7xx/str7xx_serial.c
//
//      ST STR7XX Serial I/O Interface Module
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2008 Free Software Foundation, Inc.
// Copyright (C) 2004, 2005, 2006, 2008 eCosCentric Limited                 
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):     nickg
// Date:          2005-03-24
// Purpose:       ST STR7XX Serial I/O module
// Description: 
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/hal.h>
#include <pkgconf/infra.h>
#include <pkgconf/system.h>
#include <pkgconf/io_serial.h>
#include <pkgconf/io.h>
#include <pkgconf/kernel.h>

#include <cyg/io/io.h>
#include <cyg/hal/hal_io.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_cache.h>
#include <cyg/io/devtab.h>
#include <cyg/io/serial.h>
#include <cyg/infra/diag.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>


#ifdef CYGPKG_IO_SERIAL_ARM_STR7XX

#include "str7xx_serial.h"

//#undef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW

//==========================================================================

typedef struct str7xx_serial_info
{
    CYG_WORD       uart;
    CYG_ADDRWORD   base;
    CYG_WORD       int_num;
    cyg_int32      rx_pio;
    cyg_int32      tx_pio;
    cyg_int32      rts_pio;
    cyg_int32      cts_pio;
    CYG_WORD       cts_intnum;
    cyg_uint32     dcd_pio;
    cyg_uint32     dcd_intnum;
    cyg_uint32     dtr_pio;
    cyg_int32      cts_last_val;
    cyg_int32      dcd_last_val;
    cyg_interrupt  serial_interrupt;
    cyg_handle_t   serial_interrupt_handle;
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW    
    cyg_interrupt  cts_interrupt;
    cyg_handle_t   cts_interrupt_handle;
#endif
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
    cyg_interrupt  dcd_interrupt;
    cyg_handle_t   dcd_interrupt_handle;
#endif
} str7xx_serial_info;

//==========================================================================

#define UART_PIO( __port, __bit, __pol ) ((__port<<16)|(__pol<<4)|(__bit))
#define UART_PIO_NONE -1

#define UART_PIO_PORT(__pio) (((__pio)>>16)&3)
#define UART_PIO_BIT(__pio) ((__pio)&0xF)
#define UART_PIO_POL(__pio) (((__pio)>>4)&1)
#define UART_PIO_ACTIVE(__pio) UART_PIO_POL(__pio)
#define UART_PIO_INACTIVE(__pio) (UART_PIO_POL(__pio)^1)


//==========================================================================

static bool str7xx_serial_init(struct cyg_devtab_entry *tab);
static bool str7xx_serial_putc_interrupt(serial_channel *chan, unsigned char c);
#if (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL0) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL1) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL2) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL3) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BUFSIZE == 0)
static bool str7xx_serial_putc_polled(serial_channel *chan, unsigned char c);
#endif
static Cyg_ErrNo str7xx_serial_lookup(struct cyg_devtab_entry **tab, 
                                    struct cyg_devtab_entry *sub_tab,
                                    const char *name);
static unsigned char str7xx_serial_getc_interrupt(serial_channel *chan);
#if (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL0) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL1) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL2) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL3) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BUFSIZE == 0)
static unsigned char str7xx_serial_getc_polled(serial_channel *chan);
#endif
static Cyg_ErrNo str7xx_serial_set_config(serial_channel *chan, cyg_uint32 key,
                                        const void *xbuf, cyg_uint32 *len);
static void str7xx_serial_start_xmit(serial_channel *chan);
static void str7xx_serial_stop_xmit(serial_channel *chan);

static cyg_uint32 str7xx_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void       str7xx_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);

#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
static cyg_uint32 str7xx_CTS_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void       str7xx_CTS_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
#endif
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
static cyg_uint32 str7xx_DCD_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void       str7xx_DCD_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
#endif

//==========================================================================

#if (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL0) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE > 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL1) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE > 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL2) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BUFSIZE > 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL3) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BUFSIZE > 0)
static SERIAL_FUNS(str7xx_serial_funs_interrupt, 
                   str7xx_serial_putc_interrupt, 
                   str7xx_serial_getc_interrupt,
                   str7xx_serial_set_config,
                   str7xx_serial_start_xmit,
                   str7xx_serial_stop_xmit
    );
#endif

#if (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL0) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL1) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL2) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL3) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BUFSIZE == 0)
static SERIAL_FUNS(str7xx_serial_funs_polled, 
                   str7xx_serial_putc_polled, 
                   str7xx_serial_getc_polled,
                   str7xx_serial_set_config,
                   str7xx_serial_start_xmit,
                   str7xx_serial_stop_xmit
    );
#endif

//==========================================================================

#ifdef CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL0
static str7xx_serial_info str7xx_serial_info0 = {
    uart            : 0,
    base            : (CYG_ADDRWORD) CYGARC_HAL_STR7XX_UART0_BASE,
    int_num         : CYGNUM_HAL_INTERRUPT_UART0,
    rx_pio          : UART_PIO(0, 8, 0),
    tx_pio          : UART_PIO(0, 9, 0),
    rts_pio         : CYGHWR_HAL_ARM_STR7XX_UART0_RTS,
    cts_pio         : CYGHWR_HAL_ARM_STR7XX_UART0_CTS,
    cts_intnum      : CYGHWR_HAL_ARM_STR7XX_UART0_CTS_INT,
#ifdef CYGHWR_HAL_ARM_STR7XX_UART0_DCD
    dcd_pio         : CYGHWR_HAL_ARM_STR7XX_UART0_DCD,
    dcd_intnum      : CYGHWR_HAL_ARM_STR7XX_UART0_DCD_INT,
#else
    dcd_pio         : UART_PIO_NONE,
    dcd_intnum      : 0,
#endif
#ifdef CYGHWR_HAL_ARM_STR7XX_UART0_DTR
    dtr_pio         : CYGHWR_HAL_ARM_STR7XX_UART0_DTR,
#else
    dtr_pio         : UART_PIO_NONE,
#endif
};

#if CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE > 0
static unsigned char str7xx_serial_out_buf0[CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE];
static unsigned char str7xx_serial_in_buf0[CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE];

static SERIAL_CHANNEL_USING_INTERRUPTS(str7xx_serial_channel0,
                                       str7xx_serial_funs_interrupt, 
                                       str7xx_serial_info0,
                                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BAUD),
                                       CYG_SERIAL_STOP_DEFAULT,
                                       CYG_SERIAL_PARITY_DEFAULT,
                                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
                                       CYG_SERIAL_FLAGS_DEFAULT,
                                       &str7xx_serial_out_buf0[0], sizeof(str7xx_serial_out_buf0),
                                       &str7xx_serial_in_buf0[0], sizeof(str7xx_serial_in_buf0)
    );
#else
static SERIAL_CHANNEL(str7xx_serial_channel0,
                      str7xx_serial_funs_polled, 
                      str7xx_serial_info0,
                      CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BAUD),
                      CYG_SERIAL_STOP_DEFAULT,
                      CYG_SERIAL_PARITY_DEFAULT,
                      CYG_SERIAL_WORD_LENGTH_DEFAULT,
                      CYG_SERIAL_FLAGS_DEFAULT
    );
#endif

DEVTAB_ENTRY(str7xx_serial_io0, 
             CYGDAT_IO_SERIAL_ARM_STR7XX_SERIAL0_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_serial_devio, 
             str7xx_serial_init, 
             str7xx_serial_lookup,     // Serial driver may need initializing
             &str7xx_serial_channel0
    );
#endif //  CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL0

//==========================================================================

#ifdef CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL1
static str7xx_serial_info str7xx_serial_info1 = {
    uart            : 1,
    base            : (CYG_ADDRWORD) CYGARC_HAL_STR7XX_UART1_BASE,
    int_num         : CYGNUM_HAL_INTERRUPT_UART1,
    rx_pio          : UART_PIO(0, 10, 0),
    tx_pio          : UART_PIO(0, 11, 0),
    rts_pio         : CYGHWR_HAL_ARM_STR7XX_UART1_RTS,
    cts_pio         : CYGHWR_HAL_ARM_STR7XX_UART1_CTS,
    cts_intnum      : CYGHWR_HAL_ARM_STR7XX_UART1_CTS_INT,
#ifdef CYGHWR_HAL_ARM_STR7XX_UART1_DCD    
    dcd_pio         : CYGHWR_HAL_ARM_STR7XX_UART1_DCD,
    dcd_intnum      : CYGHWR_HAL_ARM_STR7XX_UART1_DCD_INT,
#else
    dcd_pio         : UART_PIO_NONE,
    dcd_intnum      : 0,
#endif
#ifdef CYGHWR_HAL_ARM_STR7XX_UART1_DTR
    dtr_pio         : CYGHWR_HAL_ARM_STR7XX_UART1_DTR,
#else
    dtr_pio         : UART_PIO_NONE,
#endif
};

#if CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE > 0
static unsigned char str7xx_serial_out_buf1[CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE];
static unsigned char str7xx_serial_in_buf1[CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE];

static SERIAL_CHANNEL_USING_INTERRUPTS(str7xx_serial_channel1,
                                       str7xx_serial_funs_interrupt, 
                                       str7xx_serial_info1,
                                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BAUD),
                                       CYG_SERIAL_STOP_DEFAULT,
                                       CYG_SERIAL_PARITY_DEFAULT,
                                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
                                       CYG_SERIAL_FLAGS_DEFAULT,
                                       &str7xx_serial_out_buf1[0], sizeof(str7xx_serial_out_buf1),
                                       &str7xx_serial_in_buf1[0], sizeof(str7xx_serial_in_buf1)
    );
#else
static SERIAL_CHANNEL(str7xx_serial_channel1,
                      str7xx_serial_funs_polled, 
                      str7xx_serial_info1,
                      CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BAUD),
                      CYG_SERIAL_STOP_DEFAULT,
                      CYG_SERIAL_PARITY_DEFAULT,
                      CYG_SERIAL_WORD_LENGTH_DEFAULT,
                      CYG_SERIAL_FLAGS_DEFAULT
    );
#endif

DEVTAB_ENTRY(str7xx_serial_io1, 
             CYGDAT_IO_SERIAL_ARM_STR7XX_SERIAL1_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_serial_devio, 
             str7xx_serial_init, 
             str7xx_serial_lookup,     // Serial driver may need initializing
             &str7xx_serial_channel1
    );
#endif //  CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL1

//==========================================================================

#ifdef CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL2
static str7xx_serial_info str7xx_serial_info2 = {
    uart            : 2,
    base            : (CYG_ADDRWORD) CYGARC_HAL_STR7XX_UART2_BASE,
    int_num         : CYGNUM_HAL_INTERRUPT_UART2,
    rx_pio          : UART_PIO(0, 13, 0),
    tx_pio          : UART_PIO(0, 14, 0),
    rts_pio         : CYGHWR_HAL_ARM_STR7XX_UART2_RTS,
    cts_pio         : CYGHWR_HAL_ARM_STR7XX_UART2_CTS,
    cts_intnum      : CYGHWR_HAL_ARM_STR7XX_UART2_CTS_INT,
#ifdef CYGHWR_HAL_ARM_STR7XX_UART2_DCD    
    dcd_pio         : CYGHWR_HAL_ARM_STR7XX_UART2_DCD,
    dcd_intnum      : CYGHWR_HAL_ARM_STR7XX_UART2_DCD_INT,
#else
    dcd_pio         : UART_PIO_NONE,
    dcd_intnum      : 0,
#endif
#ifdef CYGHWR_HAL_ARM_STR7XX_UART2_DTR
    dtr_pio         : CYGHWR_HAL_ARM_STR7XX_UART2_DTR,
#else
    dtr_pio         : UART_PIO_NONE,
#endif
};

#if CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BUFSIZE > 0
static unsigned char str7xx_serial_out_buf2[CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BUFSIZE];
static unsigned char str7xx_serial_in_buf2[CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BUFSIZE];

static SERIAL_CHANNEL_USING_INTERRUPTS(str7xx_serial_channel2,
                                       str7xx_serial_funs_interrupt, 
                                       str7xx_serial_info2,
                                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BAUD),
                                       CYG_SERIAL_STOP_DEFAULT,
                                       CYG_SERIAL_PARITY_DEFAULT,
                                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
                                       CYG_SERIAL_FLAGS_DEFAULT,
                                       &str7xx_serial_out_buf2[0], sizeof(str7xx_serial_out_buf2),
                                       &str7xx_serial_in_buf2[0], sizeof(str7xx_serial_in_buf2)
    );
#else
static SERIAL_CHANNEL(str7xx_serial_channel2,
                      str7xx_serial_funs_polled, 
                      str7xx_serial_info2,
                      CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BAUD),
                      CYG_SERIAL_STOP_DEFAULT,
                      CYG_SERIAL_PARITY_DEFAULT,
                      CYG_SERIAL_WORD_LENGTH_DEFAULT,
                      CYG_SERIAL_FLAGS_DEFAULT
    );
#endif

DEVTAB_ENTRY(str7xx_serial_io2, 
             CYGDAT_IO_SERIAL_ARM_STR7XX_SERIAL2_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_serial_devio, 
             str7xx_serial_init, 
             str7xx_serial_lookup,     // Serial driver may need initializing
             &str7xx_serial_channel2
    );
#endif //  CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL2

//==========================================================================

#ifdef CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL3
static str7xx_serial_info str7xx_serial_info3 = {
    uart            : 3,
    base            : (CYG_ADDRWORD) CYGARC_HAL_STR7XX_UART3_BASE,
    int_num         : CYGNUM_HAL_INTERRUPT_UART3,
    rx_pio          : UART_PIO(0, 1, 0),
    tx_pio          : UART_PIO(0, 0, 0),
    rts_pio         : CYGHWR_HAL_ARM_STR7XX_UART3_RTS,
    cts_pio         : CYGHWR_HAL_ARM_STR7XX_UART3_CTS,
    cts_intnum      : CYGHWR_HAL_ARM_STR7XX_UART3_CTS_INT,
#ifdef CYGHWR_HAL_ARM_STR7XX_UART3_DCD    
    dcd_pio         : CYGHWR_HAL_ARM_STR7XX_UART3_DCD,
    dcd_intnum      : CYGHWR_HAL_ARM_STR7XX_UART3_DCD_INT,
#else
    dcd_pio         : UART_PIO_NONE,
    dcd_intnum      : 0,
#endif
#ifdef CYGHWR_HAL_ARM_STR7XX_UART3_DTR
    dtr_pio         : CYGHWR_HAL_ARM_STR7XX_UART3_DTR,
#else
    dtr_pio         : UART_PIO_NONE,
#endif
};

#if CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BUFSIZE > 0
static unsigned char str7xx_serial_out_buf3[CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BUFSIZE];
static unsigned char str7xx_serial_in_buf3[CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BUFSIZE];

static SERIAL_CHANNEL_USING_INTERRUPTS(str7xx_serial_channel3,
                                       str7xx_serial_funs_interrupt, 
                                       str7xx_serial_info3,
                                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BAUD),
                                       CYG_SERIAL_STOP_DEFAULT,
                                       CYG_SERIAL_PARITY_DEFAULT,
                                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
                                       CYG_SERIAL_FLAGS_DEFAULT,
                                       &str7xx_serial_out_buf3[0], sizeof(str7xx_serial_out_buf3),
                                       &str7xx_serial_in_buf3[0], sizeof(str7xx_serial_in_buf3)
    );
#else
static SERIAL_CHANNEL(str7xx_serial_channel3,
                      str7xx_serial_funs_polled, 
                      str7xx_serial_info3,
                      CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BAUD),
                      CYG_SERIAL_STOP_DEFAULT,
                      CYG_SERIAL_PARITY_DEFAULT,
                      CYG_SERIAL_WORD_LENGTH_DEFAULT,
                      CYG_SERIAL_FLAGS_DEFAULT
    );
#endif

DEVTAB_ENTRY(str7xx_serial_io3, 
             CYGDAT_IO_SERIAL_ARM_STR7XX_SERIAL3_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_serial_devio, 
             str7xx_serial_init, 
             str7xx_serial_lookup,     // Serial driver may need initializing
             &str7xx_serial_channel3
    );
#endif //  CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL3


//==========================================================================
// Internal function to actually configure the hardware to desired baud
// rate, etc.

static bool
str7xx_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
{
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *)chan->dev_priv;
    const CYG_ADDRWORD base = str7xx_chan->base;
    cyg_uint32 parity = select_parity[new_config->parity];
    cyg_uint32 word_length = select_word_length[new_config->word_length-CYGNUM_SERIAL_WORD_LENGTH_5];
    cyg_uint32 stop_bits = select_stop_bits[new_config->stop];
    cyg_uint32 cr = 0;
    cyg_uint32 ier;
//    cyg_uint16 divisor;

    // Set up PIO bits

    CYGARC_HAL_STR7XX_PIO_SET( UART_PIO_PORT(str7xx_chan->tx_pio), UART_PIO_BIT(str7xx_chan->tx_pio), CYGARC_HAL_STR7XX_PIO_PP_AF );    
    CYGARC_HAL_STR7XX_PIO_SET( UART_PIO_PORT(str7xx_chan->rx_pio), UART_PIO_BIT(str7xx_chan->rx_pio), CYGARC_HAL_STR7XX_PIO_TRI_CMOS_IN );

#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW    
    if( str7xx_chan->rts_pio != UART_PIO_NONE )
    {
        // Enable the RTS line as an output and set it high.
        CYGARC_HAL_STR7XX_PIO_SET( UART_PIO_PORT(str7xx_chan->rts_pio), UART_PIO_BIT(str7xx_chan->rts_pio), CYGARC_HAL_STR7XX_PIO_PP_OUT );
        CYGARC_HAL_STR7XX_PIO_OUT( UART_PIO_PORT(str7xx_chan->rts_pio), UART_PIO_BIT(str7xx_chan->rts_pio), UART_PIO_ACTIVE(str7xx_chan->rts_pio) );
    }
    if( str7xx_chan->cts_pio != UART_PIO_NONE )
    {
        cyg_uint32 val;
        CYGARC_HAL_STR7XX_PIO_SET( UART_PIO_PORT(str7xx_chan->cts_pio), UART_PIO_BIT(str7xx_chan->cts_pio), CYGARC_HAL_STR7XX_PIO_TRI_CMOS_IN );
        CYGARC_HAL_STR7XX_PIO_IN( UART_PIO_PORT(str7xx_chan->cts_pio), UART_PIO_BIT(str7xx_chan->cts_pio), &val );
        str7xx_chan->cts_last_val = 1 & ((val&1) ^ ~UART_PIO_POL(str7xx_chan->cts_pio));
    }
#endif
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS    
    if( str7xx_chan->dcd_pio != UART_PIO_NONE )
    {
        cyg_uint32 val;
        CYGARC_HAL_STR7XX_PIO_SET( UART_PIO_PORT(str7xx_chan->dcd_pio), UART_PIO_BIT(str7xx_chan->dcd_pio), CYGARC_HAL_STR7XX_PIO_TRI_CMOS_IN );        
        CYGARC_HAL_STR7XX_PIO_IN( UART_PIO_PORT(str7xx_chan->dcd_pio), UART_PIO_BIT(str7xx_chan->dcd_pio), &val );
        str7xx_chan->dcd_last_val = 1 & ((val&1) ^ ~UART_PIO_POL(str7xx_chan->dcd_pio));        
    }
#endif

    if( str7xx_chan->dtr_pio != UART_PIO_NONE )
    {
        // Enable the DTR line as an output and set it active.
        CYGARC_HAL_STR7XX_PIO_SET( UART_PIO_PORT(str7xx_chan->dtr_pio), UART_PIO_BIT(str7xx_chan->dtr_pio), CYGARC_HAL_STR7XX_PIO_PP_OUT );        
        CYGARC_HAL_STR7XX_PIO_OUT( UART_PIO_PORT(str7xx_chan->dtr_pio), UART_PIO_BIT(str7xx_chan->dtr_pio), UART_PIO_ACTIVE(str7xx_chan->dtr_pio) );
    }
    
    // Word length and parity are combined into a single config
    // setting. The hardware only supports a limited subset of
    // combinations.

    switch( word_length+parity )
    {
    case (7<<4)+2:      // 7 bit, even parity
        cr = CYGARC_HAL_STR7XX_UART_CR_BITS_7_PAR;
        break;
    case (7<<4)+1:      // 7 bit, odd parity
        cr = CYGARC_HAL_STR7XX_UART_CR_BITS_7_PAR;
        cr |= CYGARC_HAL_STR7XX_UART_CR_ODD_PARITY;
        break;
    case (8<<4)+0:      // 8 bit, no parity
        cr = CYGARC_HAL_STR7XX_UART_CR_BITS_8;
        break;
    case (8<<4)+2:      // 8 bit, even parity
        cr = CYGARC_HAL_STR7XX_UART_CR_BITS_8_PAR;
        break;
    case (8<<4)+1:      // 8 bit, odd parity
        cr = CYGARC_HAL_STR7XX_UART_CR_BITS_8_PAR;
        cr |= CYGARC_HAL_STR7XX_UART_CR_ODD_PARITY;        
        break;
    default:
        return false;
    }

    
    if (stop_bits == 0xFF)
        return false;  // Unsupported configuration


     // disable interrupts on device and disable device
     HAL_WRITE_UINT32( base+CYGARC_HAL_STR7XX_UART_IER, 0 );
     HAL_WRITE_UINT32( base+CYGARC_HAL_STR7XX_UART_CR, 0 );

     // Set the baud via the HAL so that any changes to the frequency
     // of PCLK1 will cause the baud rate to be adjusted appropriately.
     hal_str7xx_uart_setbaud( str7xx_chan->uart, select_baud[new_config->baud] );
     
     // Set serial configuration
     cr |= stop_bits;
     HAL_WRITE_UINT32( base+CYGARC_HAL_STR7XX_UART_CR, cr );

     // Set timeout register to 16 bit times.
     HAL_WRITE_UINT32( base+CYGARC_HAL_STR7XX_UART_TOR, 16 );

     // Enable the FIFO and receiver
     cr |= CYGARC_HAL_STR7XX_UART_CR_FIFO_ENABLE;
     HAL_WRITE_UINT32( base+CYGARC_HAL_STR7XX_UART_CR, cr );     
     cr |= CYGARC_HAL_STR7XX_UART_CR_RX_ENABLE;
     HAL_WRITE_UINT32( base+CYGARC_HAL_STR7XX_UART_CR, cr );     

     // Reset the FIFOs
     HAL_WRITE_UINT32( base+CYGARC_HAL_STR7XX_UART_TxRST, 0 );     
     HAL_WRITE_UINT32( base+CYGARC_HAL_STR7XX_UART_RxRST, 0 );     

     // Now enable the UART
     cr |= CYGARC_HAL_STR7XX_UART_CR_BRG_ENABLE;
     HAL_WRITE_UINT32( base+CYGARC_HAL_STR7XX_UART_CR, cr );     

#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW     
     // If the new configuration is asking for CTS support, enable the interrupt from the CTS line
     if ( (new_config->flags & CYGNUM_SERIAL_FLOW_RTSCTS_TX) && str7xx_chan->cts_pio >= 0 )
         cyg_drv_interrupt_unmask(str7xx_chan->cts_intnum);
#endif
     
#if !((defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL0) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL1) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE == 0)   \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL2) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL2_BUFSIZE == 0)   \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL3) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL3_BUFSIZE == 0))

     // Enable receive interrupts

     HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_IER, ier );

     ier |= CYGARC_HAL_STR7XX_UART_IER_Rx_NOTEMPTY;
     ier |= CYGARC_HAL_STR7XX_UART_IER_TIMEOUT_NOTEMPTY;

     HAL_WRITE_UINT32( base + CYGARC_HAL_STR7XX_UART_IER, ier );
#endif

     if (new_config != &chan->config) {
        chan->config = *new_config;
    }

    return true;
}

//==========================================================================
// Function to initialize the device.  Called at bootstrap time.

static bool 
str7xx_serial_init(struct cyg_devtab_entry *tab)
{
    serial_channel * const chan = (serial_channel *) tab->priv;
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    int res;

#ifdef CYGDBG_IO_INIT
    diag_printf("STR7XX SERIAL init - dev: %x.%d\n", str7xx_chan->base, str7xx_chan->int_num);
#endif
    (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
    if (chan->out_cbuf.len != 0) {
        cyg_drv_interrupt_create(str7xx_chan->int_num,
                                 4,                      // Priority
                                 (cyg_addrword_t)chan,   // Data item passed to interrupt handler
                                 str7xx_serial_ISR,
                                 str7xx_serial_DSR,
                                 &str7xx_chan->serial_interrupt_handle,
                                 &str7xx_chan->serial_interrupt);
        cyg_drv_interrupt_attach(str7xx_chan->serial_interrupt_handle);
        cyg_drv_interrupt_unmask(str7xx_chan->int_num);

#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
        // If a CTS pio pin has been configured, prepare to use it for
        // flow control by setting up an interrupt object. Leave it
        // masked for now, we only unmask it when flow control is
        // enabled.
        if( str7xx_chan->cts_pio != UART_PIO_NONE )
        {
            cyg_drv_interrupt_create(str7xx_chan->cts_intnum,
                                     4,                      // Priority
                                     (cyg_addrword_t)chan,   // Data item passed to interrupt handler
                                     str7xx_CTS_ISR,
                                     str7xx_CTS_DSR,
                                     &str7xx_chan->cts_interrupt_handle,
                                     &str7xx_chan->cts_interrupt);
            // Set up the interrupt to trigger on a transition from
            // the inactive to the active state of the CTS line.
            cyg_interrupt_configure(str7xx_chan->cts_intnum, false, UART_PIO_ACTIVE(str7xx_chan->cts_pio) );            
            cyg_drv_interrupt_attach(str7xx_chan->cts_interrupt_handle);
            cyg_drv_interrupt_mask(str7xx_chan->cts_intnum);
        }
#endif
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
        // If a DCD pio pin has been configured, prepare to use it
        // by setting up an interrupt object.
        if( str7xx_chan->dcd_pio != UART_PIO_NONE )
        {
            cyg_drv_interrupt_create(str7xx_chan->dcd_intnum,
                                     4,                      // Priority
                                     (cyg_addrword_t)chan,   // Data item passed to interrupt handler
                                     str7xx_DCD_ISR,
                                     str7xx_DCD_DSR,
                                     &str7xx_chan->dcd_interrupt_handle,
                                     &str7xx_chan->dcd_interrupt);
            // Set up the interrupt to trigger on a transition from
            // the active to the inactive state of the DCD line.
            cyg_interrupt_configure(str7xx_chan->dcd_intnum, false, UART_PIO_INACTIVE(str7xx_chan->dcd_pio) );            
            cyg_drv_interrupt_attach(str7xx_chan->dcd_interrupt_handle);
            cyg_drv_interrupt_unmask(str7xx_chan->dcd_intnum);
        }
#endif
    }

    res = str7xx_serial_config_port(chan, &chan->config, true);
    return res;
}

//==========================================================================
// This routine is called when the device is "looked" up (i.e. attached)

static Cyg_ErrNo 
str7xx_serial_lookup(struct cyg_devtab_entry **tab, 
                  struct cyg_devtab_entry *sub_tab,
                  const char *name)
{
    serial_channel * const chan = (serial_channel *) (*tab)->priv;

    (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
    return ENOERR;
}

//==========================================================================
// Send a character to the device output buffer.
// Return 'true' if character is sent to device

static bool
str7xx_serial_putc_interrupt(serial_channel *chan, unsigned char c)
{
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    const CYG_ADDRWORD base = str7xx_chan->base;
    cyg_uint32 status;
    
    HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_SR, status );

    if (status & CYGARC_HAL_STR7XX_UART_IER_Tx_HALF)
    {
        HAL_WRITE_UINT32( base + CYGARC_HAL_STR7XX_UART_TxBUF, c );
        return true;
    }
    
    return false;
}

//==========================================================================

#if (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL0) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL1) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE == 0)

static bool
str7xx_serial_putc_polled(serial_channel *chan, unsigned char c)
{
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    const CYG_ADDRWORD base = str7xx_chan->base;
    cyg_uint32 status;

     do {
         HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_SR, status );
     } while ((status & CYGARC_HAL_STR7XX_UART_IER_Tx_HALF) == 0);

     HAL_WRITE_UINT32( base + CYGARC_HAL_STR7XX_UART_TxBUF, c );

    return true;
}
#endif

//==========================================================================
// Fetch a character from the device input buffer

static unsigned char 
str7xx_serial_getc_interrupt(serial_channel *chan)
{
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    const CYG_ADDRWORD base = str7xx_chan->base;
    CYG_WORD32 c;

    // Read data
    HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_RxBUF, c);
    return (unsigned char) (c&0xFF);
}

//==========================================================================

#if (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL0) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL0_BUFSIZE == 0) \
 || (defined(CYGPKG_IO_SERIAL_ARM_STR7XX_SERIAL1) && CYGNUM_IO_SERIAL_ARM_STR7XX_SERIAL1_BUFSIZE == 0)

static unsigned char 
str7xx_serial_getc_polled(serial_channel *chan)
{
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    const CYG_ADDRWORD base = str7xx_chan->base;
    cyg_uint32 stat;
    cyg_uint32 c;

    do {
        HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_SR, stat );
    } while ((stat & CYGARC_HAL_STR7XX_UART_IER_Rx_NOTEMPTY) == 0);
        
    HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_RxBUF, c);
    
    return (unsigned char) (c&0xFF);
}
#endif

//==========================================================================
// Set up the device characteristics; baud rate, etc.

static Cyg_ErrNo
str7xx_serial_set_config(serial_channel *chan, cyg_uint32 key,
                       const void *xbuf, cyg_uint32 *len)
{
    switch (key) {
    case CYG_IO_SET_CONFIG_SERIAL_INFO:
      {
        cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
        if ( *len < sizeof(cyg_serial_info_t) ) {
            return -EINVAL;
        }
        *len = sizeof(cyg_serial_info_t);
        if ( true != str7xx_serial_config_port(chan, config, false) )
            return -EINVAL;
      }
      break;

#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW

    case CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE:
      {
          str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;          
          cyg_uint32 *f = (cyg_uint32 *)xbuf;

          if ( *len < sizeof(*f) )
              return -EINVAL;
          
          if ( chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX )
          {
              str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
              if( *f )
                  CYGARC_HAL_STR7XX_PIO_OUT( UART_PIO_PORT(str7xx_chan->rts_pio), UART_PIO_BIT(str7xx_chan->rts_pio), UART_PIO_INACTIVE(str7xx_chan->rts_pio) );                  
              else
                  CYGARC_HAL_STR7XX_PIO_OUT( UART_PIO_PORT(str7xx_chan->rts_pio), UART_PIO_BIT(str7xx_chan->rts_pio), UART_PIO_ACTIVE(str7xx_chan->rts_pio) );
          }
      }
      break;
      
    case CYG_IO_SET_CONFIG_SERIAL_HW_FLOW_CONFIG:
      {
        str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
        Cyg_ErrNo result = ENOERR;

        // If the client is asking for DSR/DTR, refuse to do it.
        if (0 != (chan->config.flags & (CYGNUM_SERIAL_FLOW_DSRDTR_RX | CYGNUM_SERIAL_FLOW_DSRDTR_TX)))
        {
            chan->config.flags &= ~(CYGNUM_SERIAL_FLOW_DSRDTR_RX | CYGNUM_SERIAL_FLOW_DSRDTR_TX);
            result = -ENOSUPP;
        }

        // If the client is asking for RTS/CTS then only allow it if
        // the port has some PIO lines attached to it.
        if (0 != (chan->config.flags & (CYGNUM_SERIAL_FLOW_RTSCTS_RX | CYGNUM_SERIAL_FLOW_RTSCTS_TX)))
        {
            if( str7xx_chan->rts_pio != UART_PIO_NONE && str7xx_chan->cts_pio != UART_PIO_NONE )
            {
                chan->config.flags &= (CYGNUM_SERIAL_FLOW_RTSCTS_RX | CYGNUM_SERIAL_FLOW_RTSCTS_TX);
            }
            else
            {
                chan->config.flags &= ~(CYGNUM_SERIAL_FLOW_RTSCTS_RX | CYGNUM_SERIAL_FLOW_RTSCTS_TX);
                result = -ENOSUPP;
            }
        }
        return result;
      }
      
#endif

    case CYG_IO_SET_CONFIG_SERIAL_TERMINALREADY:
      {
          str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;          
          cyg_uint32 *f = (cyg_uint32 *)xbuf;
          
          if ( *len < sizeof(*f) )
              return -EINVAL;

          // If a DTR line is configured, set it active according to the value of *f.
          if( str7xx_chan->dtr_pio != UART_PIO_NONE )
          {
              if( *f )
                  CYGARC_HAL_STR7XX_PIO_OUT( UART_PIO_PORT(str7xx_chan->dtr_pio), UART_PIO_BIT(str7xx_chan->dtr_pio), UART_PIO_ACTIVE(str7xx_chan->dtr_pio) );
              else
                  CYGARC_HAL_STR7XX_PIO_OUT( UART_PIO_PORT(str7xx_chan->dtr_pio), UART_PIO_BIT(str7xx_chan->dtr_pio), UART_PIO_INACTIVE(str7xx_chan->dtr_pio) );                                
          }
          
          break;
      }
      
    case CYG_IO_GET_CONFIG_SERIAL_CARRIERDETECT:
      {
          str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;          
          cyg_uint32 *f = (cyg_uint32 *)xbuf;
          
          if ( *len < sizeof(*f) )
              return -EINVAL;

          // If a DCD line has been configured, read its state and report it.
          if( str7xx_chan->dcd_pio != UART_PIO_NONE )
          {
              cyg_uint32 val;
              CYGARC_HAL_STR7XX_PIO_IN( UART_PIO_PORT(str7xx_chan->dcd_pio), UART_PIO_BIT(str7xx_chan->dcd_pio), &val );        
              val = 1 & ((val&1) ^ ~UART_PIO_POL(str7xx_chan->dcd_pio));
              *f = val;
              break;
          }
          // else drop through to return -EINVAL, which will cause the
          // generic serial driver to respond for us.
      }
      
    default:
        return -EINVAL;
    }
    return ENOERR;
}

//==========================================================================
// Enable the transmitter on the device

static void
str7xx_serial_start_xmit(serial_channel *chan)
{
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    const CYG_ADDRWORD base = str7xx_chan->base;
    cyg_uint32 ier;

    HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_IER, ier );
//    ier |= CYGARC_HAL_STR7XX_UART_IER_Tx_EMPTY;
    ier |= CYGARC_HAL_STR7XX_UART_IER_Tx_HALF;
    HAL_WRITE_UINT32( base + CYGARC_HAL_STR7XX_UART_IER, ier );
}

//==========================================================================
// Disable the transmitter on the device

static void 
str7xx_serial_stop_xmit(serial_channel *chan)
{
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    const CYG_ADDRWORD base = str7xx_chan->base;
    cyg_uint32 ier;

    HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_IER, ier );
//    ier &= ~CYGARC_HAL_STR7XX_UART_IER_Tx_EMPTY;
    ier &= ~CYGARC_HAL_STR7XX_UART_IER_Tx_HALF;
    HAL_WRITE_UINT32( base + CYGARC_HAL_STR7XX_UART_IER, ier );
}

//==========================================================================
// Serial I/O - low level interrupt handler (ISR)

static cyg_uint32 
str7xx_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
    cyg_drv_interrupt_mask(vector);    
    cyg_drv_interrupt_acknowledge(vector);
    
    return CYG_ISR_CALL_DSR|CYG_ISR_HANDLED;
}

//==========================================================================
// Serial I/O - high level interrupt handler (DSR)

static void       
str7xx_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    serial_channel * const chan = (serial_channel *) data;
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    const CYG_ADDRWORD base = str7xx_chan->base;
    CYG_WORD32 stat, mask;

    HAL_READ_UINT32(base + CYGARC_HAL_STR7XX_UART_SR, stat);
    if( stat & (CYGARC_HAL_STR7XX_UART_IER_OVERRUN|CYGARC_HAL_STR7XX_UART_IER_FRAME_ERROR|CYGARC_HAL_STR7XX_UART_IER_PARITY) )
    {

    }
    HAL_READ_UINT32(base + CYGARC_HAL_STR7XX_UART_IER, mask);
    stat &= mask;


    if( stat & ( CYGARC_HAL_STR7XX_UART_IER_Rx_NOTEMPTY |
                 CYGARC_HAL_STR7XX_UART_IER_TIMEOUT_NOTEMPTY ) )
    {
        cyg_uint32 c;

        HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_SR, stat );
        while (stat & CYGARC_HAL_STR7XX_UART_IER_Rx_NOTEMPTY)
        {
        
            HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_RxBUF, c);
            if( (c & CYGARC_HAL_STR7XX_UART_RxBUF_FE) == 0 )
                (chan->callbacks->rcv_char)(chan, c&0xFF);            
            HAL_READ_UINT32( base + CYGARC_HAL_STR7XX_UART_SR, stat );
        }
    }

#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW        
    // Check the status of the CTS line and if it has changed state
    // from active to inactive since we last looked, report the fact.
    if( (chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_TX) &&
        (str7xx_chan->cts_pio != UART_PIO_NONE) )
    {
        cyg_serial_line_status_t stat;
        cyg_uint32 val;

        CYGARC_HAL_STR7XX_PIO_IN( UART_PIO_PORT(str7xx_chan->cts_pio), UART_PIO_BIT(str7xx_chan->cts_pio), &val );
        val = 1 & ((val&1) ^ ~UART_PIO_POL(str7xx_chan->cts_pio));        

        if( val != str7xx_chan->cts_last_val )
        {
            stat.which = CYGNUM_SERIAL_STATUS_FLOW;
            stat.value = val; 
            (chan->callbacks->indicate_status)(chan, &stat );
            str7xx_chan->cts_last_val = val;
        }
    }
#endif
    
//    if( stat & CYGARC_HAL_STR7XX_UART_IER_Tx_EMPTY )
    if( stat & CYGARC_HAL_STR7XX_UART_IER_Tx_HALF )
    {
            (chan->callbacks->xmt_char)(chan);        
    }

    cyg_drv_interrupt_unmask(str7xx_chan->int_num);    
}

#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW

//==========================================================================
// CTS ISR

static cyg_uint32 
str7xx_CTS_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
    cyg_drv_interrupt_mask(vector);    
    cyg_drv_interrupt_acknowledge(vector);

    return CYG_ISR_CALL_DSR|CYG_ISR_HANDLED;
}

//==========================================================================
// CTS DSR
//
// This is only triggered when the CTS line transitions from inactive
// to active.

static void       
str7xx_CTS_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    serial_channel * const chan = (serial_channel *) data;
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    cyg_serial_line_status_t stat;
    CYG_WORD32 val;

    CYGARC_HAL_STR7XX_PIO_IN( UART_PIO_PORT(str7xx_chan->cts_pio), UART_PIO_BIT(str7xx_chan->cts_pio), &val );    
    val = 1 & ((val&1) ^ ~UART_PIO_POL(str7xx_chan->cts_pio));    

    stat.which = CYGNUM_SERIAL_STATUS_FLOW;
    stat.value = 1;
    (chan->callbacks->indicate_status)(chan, &stat );
    str7xx_chan->cts_last_val = val;

    cyg_drv_interrupt_unmask(vector);        
}

#endif

//==========================================================================

#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS

//==========================================================================
// DCD ISR

static cyg_uint32 
str7xx_DCD_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
    cyg_drv_interrupt_mask(vector);    
    cyg_drv_interrupt_acknowledge(vector);

    return CYG_ISR_CALL_DSR|CYG_ISR_HANDLED;
}

//==========================================================================
// DCD DSR
//
// This is only triggered when the DCD line transitions from active
// to inactive, i.e. when the modem indicates that the line has been dropped.
// Hardware limitations mean that we cannot detect line-up events this
// way, we have to poll for those through get_config().

static void       
str7xx_DCD_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    serial_channel * const chan = (serial_channel *) data;
    str7xx_serial_info * const str7xx_chan = (str7xx_serial_info *) chan->dev_priv;
    cyg_serial_line_status_t stat;
    CYG_WORD32 val;

    CYGARC_HAL_STR7XX_PIO_IN( UART_PIO_PORT(str7xx_chan->dcd_pio), UART_PIO_BIT(str7xx_chan->dcd_pio), &val );
    val = 1 & ((val&1) ^ ~UART_PIO_POL(str7xx_chan->dcd_pio));    
    
    stat.which = CYGNUM_SERIAL_STATUS_CARRIERDETECT;
    stat.value = val;
    (chan->callbacks->indicate_status)(chan, &stat );
    str7xx_chan->dcd_last_val = val;

    cyg_drv_interrupt_unmask(vector);        
}

#endif


//==========================================================================
#endif // CYGPKG_IO_SERIAL_ARM_STR7XX
// end of str7xx_serial.c
