📄 serial.c
字号:
//==========================================================================
//
// io/serial/common/serial.c
//
// High level serial driver
//
//==========================================================================
//####COPYRIGHTBEGIN####
//
// -------------------------------------------
// The contents of this file are subject to the Red Hat eCos Public License
// Version 1.1 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.redhat.com/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations under
// the License.
//
// The Original Code is eCos - Embedded Configurable Operating System,
// released September 30, 1998.
//
// The Initial Developer of the Original Code is Red Hat.
// Portions created by Red Hat are
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
// All Rights Reserved.
// -------------------------------------------
//
//####COPYRIGHTEND####
//==========================================================================
//#####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_ErrNo 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;
chan->flow_desc.flags |= CYG_SERIAL_FLOW_IN_THROTTLED;
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
// send an xoff
if ( force || chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_RX ) {
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;
chan->flow_desc.flags &= ~CYG_SERIAL_FLOW_IN_THROTTLED;
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
// send an xon
if ( force || chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_RX ) {
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-instaneous
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;
}
// ---------------------------------------------------------------------------
// FIXME:@@@ Throughout this file there are uses of cyg_drv_cond_signal and
// cyg_drv_cond_broadcast. Does it matter which? -Jifl
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 = -EAGAIN;
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
extern int enable_diag_uart;
int _enable = enable_diag_uart;
int _time, _stime;
externC cyg_tick_count_t cyg_current_time(void);
#endif // CYGDBG_DIAG_BUF
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -