⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serial.c

📁 eCos/RedBoot for勤研ARM AnywhereII(4510) 含全部源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
//==========================================================================
//
//      io/serial/common/serial.c
//
//      High level serial driver
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2003 Gary Thomas
//
// 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.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    gthomas
// Contributors: gthomas, grante, jlarmour, jskov
// Date:         1999-02-04
// Purpose:      Top level serial driver
// Description: 
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/io.h>
#include <pkgconf/io_serial.h>

#include <cyg/io/io.h>
#include <cyg/io/devtab.h>
#include <cyg/io/serial.h>
#include <cyg/infra/cyg_ass.h>      // assertion support
#include <cyg/infra/diag.h>         // diagnostic output

static Cyg_ErrNo serial_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len);
static Cyg_ErrNo serial_read(cyg_io_handle_t handle, void *buf, cyg_uint32 *len);
static cyg_bool serial_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info);
static Cyg_ErrNo serial_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf, cyg_uint32 *len);
static Cyg_ErrNo serial_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len);

DEVIO_TABLE(cyg_io_serial_devio,
            serial_write,
            serial_read,
            serial_select,
            serial_get_config,
            serial_set_config
    );

static void serial_init(serial_channel *chan);
static void serial_xmt_char(serial_channel *chan);
static void serial_rcv_char(serial_channel *chan, unsigned char c);
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
static void serial_indicate_status(serial_channel *chan,
                                   cyg_serial_line_status_t *s);
#endif
#if CYGINT_IO_SERIAL_BLOCK_TRANSFER
static rcv_req_reply_t serial_data_rcv_req(serial_channel *chan, int avail, 
                                           int* space_avail, 
                                           unsigned char** space);
static void serial_data_rcv_done(serial_channel *chan, int chars_rcvd);
static xmt_req_reply_t serial_data_xmt_req(serial_channel *chan, int space,
                                           int* chars_avail, 
                                           unsigned char** chars);
static void serial_data_xmt_done(serial_channel *chan, int chars_sent);
# ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
SERIAL_CALLBACKS(cyg_io_serial_callbacks, 
                 serial_init, 
                 serial_xmt_char, 
                 serial_rcv_char,
                 serial_data_rcv_req,
                 serial_data_rcv_done,
                 serial_data_xmt_req,
                 serial_data_xmt_done,
                 serial_indicate_status);

# else
SERIAL_CALLBACKS(cyg_io_serial_callbacks, 
                 serial_init, 
                 serial_xmt_char, 
                 serial_rcv_char,
                 serial_data_rcv_req,
                 serial_data_rcv_done,
                 serial_data_xmt_req,
                 serial_data_xmt_done);
# endif
#else
# ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
SERIAL_CALLBACKS(cyg_io_serial_callbacks, 
                 serial_init, 
                 serial_xmt_char, 
                 serial_rcv_char,
                 serial_indicate_status);
# else
SERIAL_CALLBACKS(cyg_io_serial_callbacks, 
                 serial_init, 
                 serial_xmt_char, 
                 serial_rcv_char);
# endif
#endif

// ---------------------------------------------------------------------------

#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL

static __inline__ void
throttle_tx( serial_channel *chan )
{
    chan->flow_desc.flags |= CYG_SERIAL_FLOW_OUT_THROTTLED;
    // the throttling itself occurs in the serial_xmt_char() callback
}

static __inline__ void
restart_tx( serial_channel *chan )
{
    serial_funs *funs = chan->funs;

    chan->flow_desc.flags &= ~CYG_SERIAL_FLOW_OUT_THROTTLED;

#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
    // See if there is now enough room to say it is available
    // for writing
    {
        cbuf_t *cbuf = &chan->out_cbuf;
        int space;
        
        space = cbuf->len - cbuf->nb;
        if (space >= cbuf->low_water)
            cyg_selwakeup( &cbuf->selinfo );
    }
#endif
    if ( chan->out_cbuf.nb > 0 )
        (funs->start_xmit)(chan);
}

static __inline__ void
throttle_rx( serial_channel *chan, cyg_bool force )
{
    serial_funs *funs = chan->funs;
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
    cyg_uint32 prev_flags = chan->flow_desc.flags;
#endif

    chan->flow_desc.flags |= CYG_SERIAL_FLOW_IN_THROTTLED;
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
    // send an xoff if not already done (throttled)
    if ( force ||
        ((chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_RX) &&
         (0==(prev_flags & CYG_SERIAL_FLOW_IN_THROTTLED))) ) {
        CYG_ASSERT(force||(chan->flow_desc.xchar=='\0')||(chan->flow_desc.xchar==CYGDAT_IO_SERIAL_FLOW_CONTROL_XOFF_CHAR), "xchar already set (XOFF)");
        chan->flow_desc.xchar = CYGDAT_IO_SERIAL_FLOW_CONTROL_XOFF_CHAR;
        // Make sure xmit is running so we can send it
        (funs->start_xmit)(chan); 
    }
#endif
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
    {
        cyg_uint32 i=1;
        cyg_uint32 len = sizeof(i);
        
        // set hardware flow control - don't care if it fails
        if ( force || (chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX) ||
             (chan->config.flags & CYGNUM_SERIAL_FLOW_DSRDTR_RX) )
            (funs->set_config)(chan,
                               CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE,
                               &i, &len);
    }
#endif
}

static __inline__ void
restart_rx( serial_channel *chan, cyg_bool force )
{
    serial_funs *funs = chan->funs;
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
    cyg_uint32 prev_flags = chan->flow_desc.flags;
#endif

    chan->flow_desc.flags &= ~CYG_SERIAL_FLOW_IN_THROTTLED;
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
    // send an xon, if we haven't already
    if ( force ||
        ((chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_RX) &&
         (prev_flags & CYG_SERIAL_FLOW_IN_THROTTLED)) ) {
        CYG_ASSERT(force||(chan->flow_desc.xchar=='\0')||(chan->flow_desc.xchar==CYGDAT_IO_SERIAL_FLOW_CONTROL_XON_CHAR), "xchar already set (XON)");
        chan->flow_desc.xchar = CYGDAT_IO_SERIAL_FLOW_CONTROL_XON_CHAR;
        (funs->start_xmit)(chan);  // Make sure xmit is running so we can send it
    }
#endif
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
    {
        cyg_uint32 i=0;
        cyg_uint32 len = sizeof(i);
        
        // set hardware flow control - don't care if it fails
        if ( force || (chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX) ||
             (chan->config.flags & CYGNUM_SERIAL_FLOW_DSRDTR_RX) )
            (funs->set_config)(chan,
                               CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE,
                               &i, &len);
    }
#endif
}

#endif

// ---------------------------------------------------------------------------

static void
serial_init(serial_channel *chan)
{
    if (chan->init) return;
    if (chan->out_cbuf.len != 0) {
#ifdef CYGDBG_IO_INIT
        diag_printf("Set output buffer - buf: %x len: %d\n", chan->out_cbuf.data, chan->out_cbuf.len);
#endif
        chan->out_cbuf.waiting = false;
        chan->out_cbuf.abort = false;
#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
        chan->out_cbuf.blocking = true;
#endif
        chan->out_cbuf.pending = 0;
        cyg_drv_mutex_init(&chan->out_cbuf.lock);
        cyg_drv_cond_init(&chan->out_cbuf.wait, &chan->out_cbuf.lock);
        chan->out_cbuf.low_water = chan->out_cbuf.len / 4;
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
        cyg_selinit( &chan->out_cbuf.selinfo );
#endif        
    }
    if (chan->in_cbuf.len != 0) {
        cbuf_t *cbuf = &chan->in_cbuf;

#ifdef CYGDBG_IO_INIT
        diag_printf("Set input buffer - buf: %x len: %d\n", cbuf->data, cbuf->len);
#endif
        cbuf->waiting = false;
        cbuf->abort = false;
#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
        cbuf->blocking = true;
#endif
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
        cyg_selinit( &cbuf->selinfo );
#endif
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
        cbuf->low_water =
            (CYGNUM_IO_SERIAL_FLOW_CONTROL_LOW_WATER_PERCENT * cbuf->len) / 100;
        cbuf->high_water =
            (CYGNUM_IO_SERIAL_FLOW_CONTROL_HIGH_WATER_PERCENT * cbuf->len) / 100;
# ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
        // But make sure it is at least 35 below buffer size, to allow
        // for 16 byte fifos, twice, plus some latency before s/w flow
        // control can kick in. This doesn't apply to h/w flow control
        // as it is near-instantaneous
        if ( (cbuf->len - cbuf->high_water) < 35 )
            cbuf->high_water = cbuf->len - 35;
        // and just in case...
        if ( cbuf->high_water <= 0 )
            cbuf->high_water = 1;
        if ( cbuf->low_water > cbuf->high_water )
            cbuf->low_water = cbuf->high_water;
# endif
#endif
        cyg_drv_mutex_init(&cbuf->lock);
        cyg_drv_cond_init(&cbuf->wait, &cbuf->lock);
    }
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
    chan->status_callback = NULL;
#endif

#ifdef CYGDBG_USE_ASSERTS
#if CYGINT_IO_SERIAL_BLOCK_TRANSFER
    chan->in_cbuf.block_mode_xfer_running = false;
    chan->out_cbuf.block_mode_xfer_running = false;
#endif // CYGINT_IO_SERIAL_BLOCK_TRANSFER
#endif // CYGDBG_USE_ASSERTS
    chan->init = true;
}

// ---------------------------------------------------------------------------

static Cyg_ErrNo 
serial_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len)
{
    cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
    serial_channel *chan = (serial_channel *)t->priv;
    serial_funs *funs = chan->funs;
    cyg_int32 size = *len;
    cyg_uint8 *buf = (cyg_uint8 *)_buf;
    int next;
    cbuf_t *cbuf = &chan->out_cbuf;
    Cyg_ErrNo res = ENOERR;

    cyg_drv_mutex_lock(&cbuf->lock);
    cbuf->abort = false;

    if (cbuf->len == 0) {
        // Non interrupt driven (i.e. polled) operation
        while (size-- > 0) {
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
            while ( ( 0 != (chan->flow_desc.flags & CYG_SERIAL_FLOW_OUT_THROTTLED) ) ||
                    ((funs->putc)(chan, *buf) == false) ) 
                ;  // Ignore full, keep trying
#else
            while ((funs->putc)(chan, *buf) == false) 
                ;  // Ignore full, keep trying
#endif
            buf++;
        }
    } else {
        cyg_drv_dsr_lock();  // Avoid race condition testing pointers
        while (size > 0) {       
            next = cbuf->put + 1;
            if (next == cbuf->len) next = 0;
            if (cbuf->nb == cbuf->len) {
                cbuf->waiting = true;
                // Buffer full - wait for space
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
                if ( 0 == (chan->flow_desc.flags & CYG_SERIAL_FLOW_OUT_THROTTLED) )
#endif
                    (funs->start_xmit)(chan);  // Make sure xmit is running

                // Check flag: 'start_xmit' may have obviated the need
                // to wait :-)
                if (cbuf->waiting) {
#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
                    // Optionally return if configured for non-blocking mode.
                    if (!cbuf->blocking) {
                        *len -= size;   // number of characters actually sent
                        cbuf->waiting = false;
                        res = (*len == 0) ? -EAGAIN : ENOERR;
                        break;
                    }
#endif // CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
                    cbuf->pending += size;  // Have this much more to send [eventually]
                    if( !cyg_drv_cond_wait(&cbuf->wait) )
                        cbuf->abort = true;
                    cbuf->pending -= size;
                }
                if (cbuf->abort) {
                    // Give up!
                    *len -= size;   // number of characters actually sent
                    cbuf->abort = false;
                    cbuf->waiting = false;
                    res = -EINTR;
                    break;
                }
            } else {
                cbuf->data[cbuf->put++] = *buf++;
                cbuf->put = next;
                cbuf->nb++;
                size--;  // Only count if actually sent!
            }
        }
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
        if ( 0 == (chan->flow_desc.flags & CYG_SERIAL_FLOW_OUT_THROTTLED) )
#endif
            (funs->start_xmit)(chan);  // Start output as necessary
        cyg_drv_dsr_unlock();
    }
    cyg_drv_mutex_unlock(&cbuf->lock);
    return res;
}


// ---------------------------------------------------------------------------

static Cyg_ErrNo 
serial_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len)
{
    cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
    serial_channel *chan = (serial_channel *)t->priv;
    serial_funs *funs = chan->funs;
    cyg_uint8 *buf = (cyg_uint8 *)_buf;
    cyg_int32 size = 0;
    cbuf_t *cbuf = &chan->in_cbuf;
    Cyg_ErrNo res = ENOERR;
#ifdef XX_CYGDBG_DIAG_BUF

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -