📄 serial.c
字号:
//==========================================================================
//
// 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 + -