📄 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 outputstatic 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_STATUSstatic void serial_indicate_status(serial_channel *chan, cyg_serial_line_status_t *s);#endif#if CYGINT_IO_SERIAL_BLOCK_TRANSFERstatic 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_STATUSSERIAL_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);# elseSERIAL_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_STATUSSERIAL_CALLBACKS(cyg_io_serial_callbacks, serial_init, serial_xmt_char, serial_rcv_char, serial_indicate_status);# elseSERIAL_CALLBACKS(cyg_io_serial_callbacks, serial_init, serial_xmt_char, serial_rcv_char);# endif#endif// ---------------------------------------------------------------------------#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROLstatic __inline__ voidthrottle_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__ voidrestart_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__ voidthrottle_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__ voidrestart_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 voidserial_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 + -