OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/rtos/ecos-2.0/packages/io/serial/v2_0/src
    from Rev 27 to Rev 174
    Reverse comparison

Rev 27 → Rev 174

/common/serial.c
0,0 → 1,1175
//==========================================================================
//
// 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.
//
// 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_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
 
cyg_drv_mutex_lock(&cbuf->lock);
cbuf->abort = false;
 
if (cbuf->len == 0) {
// Non interrupt driven (i.e. polled) operation
while (size++ < *len) {
cyg_uint8 c = (funs->getc)(chan);
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
// for software flow control, if the driver returns one of the
// characters we act on it and then drop it (the app must not
// see it)
if ( chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_TX ) {
if ( c == CYGDAT_IO_SERIAL_FLOW_CONTROL_XOFF_CHAR ) {
throttle_tx( chan );
} else if ( c == CYGDAT_IO_SERIAL_FLOW_CONTROL_XON_CHAR ) {
restart_tx( chan );
}
else
*buf++ = c;
}
else
*buf++ = c;
#else
*buf++ = c;
#endif
}
} else {
cyg_drv_dsr_lock(); // Avoid races
while (size < *len) {
if (cbuf->nb > 0) {
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
if ( (cbuf->nb <= cbuf->low_water) &&
(chan->flow_desc.flags & CYG_SERIAL_FLOW_IN_THROTTLED) )
restart_rx( chan, false );
#endif
*buf++ = cbuf->data[cbuf->get];
if (++cbuf->get == cbuf->len) cbuf->get = 0;
cbuf->nb--;
size++;
} else {
#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
if (!cbuf->blocking) {
*len = size; // characters actually read
res = -EAGAIN;
break;
}
#endif // CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
cbuf->waiting = true;
#ifdef XX_CYGDBG_DIAG_BUF
enable_diag_uart = 0;
HAL_CLOCK_READ(&_time);
_stime = (int)cyg_current_time();
diag_printf("READ wait - get: %d, put: %d, time: %x.%x\n", cbuf->get, cbuf->put, _stime, _time);
enable_diag_uart = _enable;
#endif // CYGDBG_DIAG_BUF
if( !cyg_drv_cond_wait(&cbuf->wait) )
cbuf->abort = true;
#ifdef XX_CYGDBG_DIAG_BUF
enable_diag_uart = 0;
HAL_CLOCK_READ(&_time);
_stime = (int)cyg_current_time();
diag_printf("READ continue - get: %d, put: %d, time: %x.%x\n", cbuf->get, cbuf->put, _stime, _time);
enable_diag_uart = _enable;
#endif // CYGDBG_DIAG_BUF
if (cbuf->abort) {
// Give up!
*len = size; // characters actually read
cbuf->abort = false;
cbuf->waiting = false;
res = -EINTR;
break;
}
}
}
cyg_drv_dsr_unlock();
}
#ifdef XX_CYGDBG_DIAG_BUF
cyg_drv_isr_lock();
enable_diag_uart = 0;
HAL_CLOCK_READ(&_time);
_stime = (int)cyg_current_time();
diag_printf("READ done - size: %d, len: %d, time: %x.%x\n", size, *len, _stime, _time);
enable_diag_uart = _enable;
cyg_drv_isr_unlock();
#endif // CYGDBG_DIAG_BUF
cyg_drv_mutex_unlock(&cbuf->lock);
return res;
}
 
 
// ---------------------------------------------------------------------------
 
static cyg_bool
serial_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
{
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
 
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
serial_channel *chan = (serial_channel *)t->priv;
 
switch( which )
{
case CYG_FREAD:
{
cbuf_t *cbuf = &chan->in_cbuf;
 
// Check for data in the input buffer. If there is none,
// register the select operation, otherwise return true.
 
if( cbuf->nb == 0 )
cyg_selrecord( info, &cbuf->selinfo );
else return true;
}
break;
case CYG_FWRITE:
{
// Check for space in the output buffer. If there is none,
// register the select operation, otherwise return true.
 
cbuf_t *cbuf = &chan->out_cbuf;
int space = cbuf->len - cbuf->nb;
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
if ( (space < cbuf->low_water) ||
(chan->flow_desc.flags & CYG_SERIAL_FLOW_OUT_THROTTLED) )
cyg_selrecord( info, &cbuf->selinfo );
#else
if (space < cbuf->low_water)
cyg_selrecord( info, &cbuf->selinfo );
#endif
else return true;
}
break;
 
case 0: // exceptions - none supported
break;
}
return false;
#else
 
// With no select support, we simply return true.
return true;
#endif
}
 
 
// ---------------------------------------------------------------------------
 
static Cyg_ErrNo
serial_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *xbuf,
cyg_uint32 *len)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
serial_channel *chan = (serial_channel *)t->priv;
cyg_serial_info_t *buf = (cyg_serial_info_t *)xbuf;
Cyg_ErrNo res = ENOERR;
cbuf_t *out_cbuf = &chan->out_cbuf;
cbuf_t *in_cbuf = &chan->in_cbuf;
serial_funs *funs = chan->funs;
 
switch (key) {
case CYG_IO_GET_CONFIG_SERIAL_INFO:
if (*len < sizeof(cyg_serial_info_t)) {
return -EINVAL;
}
*buf = chan->config;
*len = sizeof(chan->config);
break;
 
case CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO:
// return rx/tx buffer sizes and counts
{
cyg_serial_buf_info_t *p;
if (*len < sizeof(cyg_serial_buf_info_t))
return -EINVAL;
*len = sizeof(cyg_serial_buf_info_t);
p = (cyg_serial_buf_info_t *)xbuf;
p->rx_bufsize = in_cbuf->len;
if (p->rx_bufsize)
p->rx_count = in_cbuf->nb;
else
p->rx_count = 0;
p->tx_bufsize = out_cbuf->len;
if (p->tx_bufsize)
p->tx_count = out_cbuf->nb;
else
p->tx_count = 0;
}
break;
case CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN:
// Wait for any pending output to complete
if (out_cbuf->len == 0) break; // Nothing to do if not buffered
cyg_drv_mutex_lock(&out_cbuf->lock); // Stop any further output processing
cyg_drv_dsr_lock();
while (out_cbuf->pending || (out_cbuf->nb > 0)) {
out_cbuf->waiting = true;
if(!cyg_drv_cond_wait(&out_cbuf->wait) )
res = -EINTR;
}
cyg_drv_dsr_unlock();
cyg_drv_mutex_unlock(&out_cbuf->lock);
break;
 
case CYG_IO_GET_CONFIG_SERIAL_INPUT_FLUSH:
// Flush any buffered input
if (in_cbuf->len == 0) break; // Nothing to do if not buffered
cyg_drv_mutex_lock(&in_cbuf->lock); // Stop any further input processing
cyg_drv_dsr_lock();
if (in_cbuf->waiting) {
in_cbuf->abort = true;
cyg_drv_cond_signal(&in_cbuf->wait);
in_cbuf->waiting = false;
}
in_cbuf->get = in_cbuf->put = in_cbuf->nb = 0; // Flush buffered input
cyg_drv_dsr_unlock();
cyg_drv_mutex_unlock(&in_cbuf->lock);
break;
 
case CYG_IO_GET_CONFIG_SERIAL_ABORT:
// Abort any outstanding I/O, including blocked reads
// Caution - assumed to be called from 'timeout' (i.e. DSR) code
if (in_cbuf->len != 0) {
in_cbuf->abort = true;
cyg_drv_cond_signal(&in_cbuf->wait);
}
if (out_cbuf->len != 0) {
out_cbuf->abort = true;
cyg_drv_cond_signal(&out_cbuf->wait);
}
break;
 
case CYG_IO_GET_CONFIG_SERIAL_OUTPUT_FLUSH:
// Throw away any pending output
if (out_cbuf->len == 0) break; // Nothing to do if not buffered
cyg_drv_mutex_lock(&out_cbuf->lock); // Stop any further output processing
cyg_drv_dsr_lock();
if (out_cbuf->nb > 0) {
out_cbuf->get = out_cbuf->put = out_cbuf->nb = 0; // Empties queue!
(funs->stop_xmit)(chan); // Done with transmit
}
if (out_cbuf->waiting) {
out_cbuf->abort = true;
cyg_drv_cond_signal(&out_cbuf->wait);
out_cbuf->waiting = false;
}
cyg_drv_dsr_unlock();
cyg_drv_mutex_unlock(&out_cbuf->lock);
break;
 
#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
case CYG_IO_GET_CONFIG_READ_BLOCKING:
if (*len < sizeof(cyg_uint32)) {
return -EINVAL;
}
*(cyg_uint32*)xbuf = (in_cbuf->blocking) ? 1 : 0;
break;
 
case CYG_IO_GET_CONFIG_WRITE_BLOCKING:
if (*len < sizeof(cyg_uint32)) {
return -EINVAL;
}
*(cyg_uint32*)xbuf = (out_cbuf->blocking) ? 1 : 0;
break;
#endif // CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
 
default:
res = -EINVAL;
}
return res;
}
 
 
// ---------------------------------------------------------------------------
 
static Cyg_ErrNo
serial_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *xbuf,
cyg_uint32 *len)
{
Cyg_ErrNo res = ENOERR;
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
serial_channel *chan = (serial_channel *)t->priv;
#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
cbuf_t *out_cbuf = &chan->out_cbuf;
cbuf_t *in_cbuf = &chan->in_cbuf;
#endif
serial_funs *funs = chan->funs;
 
switch (key) {
#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
case CYG_IO_SET_CONFIG_READ_BLOCKING:
if (*len < sizeof(cyg_uint32) || 0 == in_cbuf->len) {
return -EINVAL;
}
in_cbuf->blocking = (1 == *(cyg_uint32*)xbuf) ? true : false;
break;
case CYG_IO_SET_CONFIG_WRITE_BLOCKING:
if (*len < sizeof(cyg_uint32) || 0 == out_cbuf->len) {
return -EINVAL;
}
out_cbuf->blocking = (1 == *(cyg_uint32*)xbuf) ? true : false;
break;
#endif // CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
 
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
case CYG_IO_SET_CONFIG_SERIAL_FLOW_CONTROL_METHOD:
{
cyg_uint32 *f = (cyg_uint32 *)xbuf;
 
if (*len < sizeof(*f))
return -EINVAL;
 
cyg_drv_dsr_lock();
 
chan->config.flags &= ~(CYGNUM_SERIAL_FLOW_XONXOFF_RX|
CYGNUM_SERIAL_FLOW_XONXOFF_TX|
CYGNUM_SERIAL_FLOW_RTSCTS_RX|
CYGNUM_SERIAL_FLOW_RTSCTS_TX|
CYGNUM_SERIAL_FLOW_DSRDTR_RX|
CYGNUM_SERIAL_FLOW_DSRDTR_TX);
chan->config.flags |= (*f & (
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
CYGNUM_SERIAL_FLOW_XONXOFF_RX|
CYGNUM_SERIAL_FLOW_XONXOFF_TX|
#endif
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
CYGNUM_SERIAL_FLOW_RTSCTS_RX|
CYGNUM_SERIAL_FLOW_RTSCTS_TX|
CYGNUM_SERIAL_FLOW_DSRDTR_RX|
CYGNUM_SERIAL_FLOW_DSRDTR_TX|
#endif
0));
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
// up to hardware driver to clear flags if rejected
res = (funs->set_config)(chan,
CYG_IO_SET_CONFIG_SERIAL_HW_FLOW_CONFIG,
NULL, NULL);
#endif
cyg_drv_dsr_unlock();
}
break;
 
case CYG_IO_SET_CONFIG_SERIAL_FLOW_CONTROL_FORCE:
{
cyg_uint32 *f = (cyg_uint32 *)xbuf;
 
if (*len < sizeof(*f))
return -EINVAL;
cyg_drv_dsr_lock();
switch (*f) {
case CYGNUM_SERIAL_FLOW_THROTTLE_RX:
throttle_rx( chan, true );
break;
case CYGNUM_SERIAL_FLOW_RESTART_RX:
restart_rx( chan, true );
break;
case CYGNUM_SERIAL_FLOW_THROTTLE_TX:
throttle_tx( chan );
break;
case CYGNUM_SERIAL_FLOW_RESTART_TX:
restart_tx( chan );
break;
default:
res = -EINVAL;
break;
}
cyg_drv_dsr_unlock();
}
break;
#endif // CYGPKG_IO_SERIAL_FLOW_CONTROL
 
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
case CYG_IO_SET_CONFIG_SERIAL_STATUS_CALLBACK:
{
cyg_serial_line_status_callback_fn_t newfn;
CYG_ADDRWORD newpriv;
cyg_serial_line_status_callback_t *tmp =
(cyg_serial_line_status_callback_t *)xbuf;
if ( *len < sizeof(*tmp) )
return -EINVAL;
 
newfn = tmp->fn;
newpriv = tmp->priv;
 
// prevent callbacks while we do this
cyg_drv_dsr_lock();
// store old callbacks in same structure
tmp->fn = chan->status_callback;
tmp->priv = chan->status_callback_priv;
chan->status_callback = newfn;
chan->status_callback_priv = newpriv;
cyg_drv_dsr_unlock();
*len = sizeof(*tmp);
}
break;
#endif
 
default:
// pass down to lower layers
return (funs->set_config)(chan, key, xbuf, len);
}
return res;
}
 
// ---------------------------------------------------------------------------
 
static void
serial_xmt_char(serial_channel *chan)
{
cbuf_t *cbuf = &chan->out_cbuf;
serial_funs *funs = chan->funs;
unsigned char c;
int space;
 
#if CYGINT_IO_SERIAL_BLOCK_TRANSFER
CYG_ASSERT(false == cbuf->block_mode_xfer_running,
"Attempting char xmt while block transfer is running");
#endif
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
// if we are required to send an XON/XOFF char, send it before
// anything else
// FIXME: what if XON gets corrupted in transit to the other end?
// Should we resend XON even though the other end may not be wanting
// to send us stuff at this point?
if ( chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_RX ) {
if ( chan->flow_desc.xchar ) {
if ( (funs->putc)(chan, chan->flow_desc.xchar) ) {
chan->flow_desc.xchar = '\0';
} else { // otherwise there's no space and we have to wait
return;
}
}
}
#endif
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
// if we're meant to be throttled, just stop and leave
if ( chan->flow_desc.flags & CYG_SERIAL_FLOW_OUT_THROTTLED ) {
(funs->stop_xmit)(chan); // Stop transmitting for now
return;
}
#endif
while (cbuf->nb > 0) {
c = cbuf->data[cbuf->get];
if ((funs->putc)(chan, c)) {
cbuf->get++;
if (cbuf->get == cbuf->len) cbuf->get = 0;
cbuf->nb--;
} else {
// See if there is now enough room to restart writer
space = cbuf->len - cbuf->nb;
if (space >= cbuf->low_water) {
if (cbuf->waiting) {
cbuf->waiting = false;
cyg_drv_cond_broadcast(&cbuf->wait);
}
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
cyg_selwakeup( &cbuf->selinfo );
#endif
}
return; // Need to wait for more space
}
}
(funs->stop_xmit)(chan); // Done with transmit
 
// must signal waiters, and wake up selecters for the case when
// this was the last char to be sent and they hadn't been signalled
// before (e.g. because of flow control)
if (cbuf->waiting) {
cbuf->waiting = false;
cyg_drv_cond_signal(&cbuf->wait);
}
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
cyg_selwakeup( &cbuf->selinfo );
#endif
}
 
// ---------------------------------------------------------------------------
 
static void
serial_rcv_char(serial_channel *chan, unsigned char c)
{
cbuf_t *cbuf = &chan->in_cbuf;
 
#if CYGINT_IO_SERIAL_BLOCK_TRANSFER
CYG_ASSERT(false == cbuf->block_mode_xfer_running,
"Attempting char rcv while block transfer is running");
#endif
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
// for software flow control, if the driver returns one of the characters
// we act on it and then drop it (the app must not see it)
if ( chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_TX ) {
if ( c == CYGDAT_IO_SERIAL_FLOW_CONTROL_XOFF_CHAR ) {
throttle_tx( chan );
return; // it wasn't a "real" character
} else if ( c == CYGDAT_IO_SERIAL_FLOW_CONTROL_XON_CHAR ) {
restart_tx( chan );
return; // it wasn't a "real" character
}
}
#endif
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
// If we've hit the high water mark, tell the other side to stop
if ( cbuf->nb >= cbuf->high_water ) {
throttle_rx( chan, false );
}
#endif
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
// Wake up any pending selectors if we are about to
// put some data into a previously empty buffer.
if( cbuf->nb == 0 )
cyg_selwakeup( &cbuf->selinfo );
#endif
 
// If the flow control is not enabled/sufficient and the buffer is
// already full, just throw new characters away.
 
if ( cbuf->nb < cbuf->len ) {
cbuf->data[cbuf->put++] = c;
if (cbuf->put == cbuf->len) cbuf->put = 0;
cbuf->nb++;
} // note trailing else
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
else {
// Overrun. Report the error.
cyg_serial_line_status_t stat;
stat.which = CYGNUM_SERIAL_STATUS_OVERRUNERR;
serial_indicate_status(chan, &stat);
}
#endif
 
if (cbuf->waiting) {
#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);
enable_diag_uart = 0;
HAL_CLOCK_READ(&_time);
_stime = (int)cyg_current_time();
diag_printf("Signal reader - time: %x.%x\n", _stime, _time);
enable_diag_uart = _enable;
#endif // CYGDBG_DIAG_BUF
cbuf->waiting = false;
cyg_drv_cond_signal(&cbuf->wait);
}
}
 
//----------------------------------------------------------------------------
// Flow control indication callback
 
#ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
static void
serial_indicate_status(serial_channel *chan, cyg_serial_line_status_t *s )
{
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
if ( CYGNUM_SERIAL_STATUS_FLOW == s->which ) {
if ( s->value )
restart_tx( chan );
else
throttle_tx( chan );
}
#endif
if ( chan->status_callback )
(*chan->status_callback)(s, chan->status_callback_priv);
}
#endif // ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
 
//----------------------------------------------------------------------------
// Block transfer functions. Not all drivers require these. Those that
// do must follow the required semantics:
//
// Attempt to transfer as much via the block transfer function as
// possible, _but_ if that fails, do the remaining bytes via the
// single-char function. That ensures that all policy decisions can be
// made in this driver, and not in the device driver.
//
// Note: if the driver uses DMA for transmission, an initial failing
// call to the xmt_req function must cause the start_xmit function to
// fall-back to regular CPU-interrupt based single-character
// transmission.
 
#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)
{
cbuf_t *cbuf = &chan->in_cbuf;
int gap;
 
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
// When there is software flow-control, force the serial device
// driver to use the single-char xmt/rcv functions, since these
// have to make policy decision based on the data. Rcv function
// may also have to transmit data to throttle the xmitter.
if (chan->config.flags & (CYGNUM_SERIAL_FLOW_XONXOFF_TX|CYGNUM_SERIAL_FLOW_XONXOFF_RX))
return CYG_RCV_DISABLED;
#endif
 
CYG_ASSERT(false == cbuf->block_mode_xfer_running,
"Attempting new block transfer while another is running");
// Check for space
gap = cbuf->nb;
if (gap == cbuf->len)
return CYG_RCV_FULL;
 
#ifdef CYGDBG_USE_ASSERTS
cbuf->block_mode_xfer_running = true;
#endif
 
if (0 == gap) {
// Buffer is empty. Reset put/get indexes to get max transfer in
// one chunk.
cbuf->get = 0;
cbuf->put = 0;
gap = cbuf->len;
} else {
// Free space (G = get, P = put, x = data, . = empty)
// positive: xxxxP.....Gxxx
// negative: ..GxxxxxP..... [offer last chunk only]
 
// First try for a gap between put and get locations
gap = cbuf->get - cbuf->put;
if (gap < 0) {
// If failed, the gap is between put and the end of buffer
gap = cbuf->len - cbuf->put;
}
}
 
if (avail < gap) gap = avail; // bound by what's available from hw
*space_avail = gap;
*space = &cbuf->data[cbuf->put];
 
CYG_ASSERT((gap+cbuf->nb) <= cbuf->len, "Buffer will overflow");
CYG_ASSERT(cbuf->put < cbuf->len, "Invalid put ptr");
CYG_ASSERT(cbuf->get < cbuf->len, "Invalid get ptr");
 
return CYG_RCV_OK;
}
 
static void
serial_data_rcv_done(serial_channel *chan, int chars_rcvd)
{
cbuf_t *cbuf = &chan->in_cbuf;
 
cbuf->put += chars_rcvd;
cbuf->nb += chars_rcvd;
 
if (cbuf->put == cbuf->len) cbuf->put = 0;
 
CYG_ASSERT(cbuf->nb <= cbuf->len, "Buffer overflow");
CYG_ASSERT(cbuf->put < cbuf->len, "Invalid put ptr");
CYG_ASSERT(cbuf->get < cbuf->len, "Invalid get ptr");
 
if (cbuf->waiting) {
cbuf->waiting = false;
cyg_drv_cond_signal(&cbuf->wait);
}
#ifdef CYGPKG_IO_SERIAL_FLOW_CONTROL
// If we've hit the high water mark, tell the other side to stop
if ( cbuf->nb >= cbuf->high_water ) {
throttle_rx( chan, false );
}
#endif
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
// Wake up any pending selectors if we have
// put some data into a previously empty buffer.
if (chars_rcvd == cbuf->nb)
cyg_selwakeup( &cbuf->selinfo );
#endif
 
#ifdef CYGDBG_USE_ASSERTS
cbuf->block_mode_xfer_running = false;
#endif
}
 
static xmt_req_reply_t
serial_data_xmt_req(serial_channel *chan, int space,
int* chars_avail, unsigned char** chars)
{
cbuf_t *cbuf = &chan->out_cbuf;
int avail;
 
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
// When there is software flow-control, force the serial device
// driver to use the single-char xmt/rcv functions, since these
// have to make policy decision based on the data. Rcv function
// may also have to transmit data to throttle the xmitter.
if (chan->config.flags & (CYGNUM_SERIAL_FLOW_XONXOFF_TX|CYGNUM_SERIAL_FLOW_XONXOFF_RX))
return CYG_XMT_DISABLED;
#endif
 
CYG_ASSERT(false == cbuf->block_mode_xfer_running,
"Attempting new block transfer while another is running");
 
// Available data (G = get, P = put, x = data, . = empty)
// 0: no data
// negative: xxxxP.....Gxxx [offer last chunk only]
// positive: ..GxxxxxP.....
if (0 == cbuf->nb)
return CYG_XMT_EMPTY;
 
#ifdef CYGDBG_USE_ASSERTS
cbuf->block_mode_xfer_running = true;
#endif
 
if (cbuf->get >= cbuf->put) {
avail = cbuf->len - cbuf->get;
} else {
avail = cbuf->put - cbuf->get;
}
 
if (avail > space) avail = space; // bound by space in hardware
*chars_avail = avail;
*chars = &cbuf->data[cbuf->get];
 
CYG_ASSERT(avail <= cbuf->len, "Avail overflow");
CYG_ASSERT(cbuf->nb <= cbuf->len, "Buffer overflow");
CYG_ASSERT(cbuf->put < cbuf->len, "Invalid put ptr");
CYG_ASSERT(cbuf->get < cbuf->len, "Invalid get ptr");
 
return CYG_XMT_OK;
}
 
static void
serial_data_xmt_done(serial_channel *chan, int chars_sent)
{
cbuf_t *cbuf = &chan->out_cbuf;
serial_funs *funs = chan->funs;
int space;
 
cbuf->get += chars_sent;
cbuf->nb -= chars_sent;
 
if (cbuf->get == cbuf->len) cbuf->get = 0;
 
CYG_ASSERT(cbuf->nb <= cbuf->len, "Buffer overflow");
CYG_ASSERT(cbuf->nb >= 0, "Buffer underflow");
CYG_ASSERT(cbuf->put < cbuf->len, "Invalid put ptr");
CYG_ASSERT(cbuf->get < cbuf->len, "Invalid get ptr");
 
if (0 == cbuf->nb) {
(funs->stop_xmit)(chan); // Done with transmit
cbuf->get = cbuf->put = 0; // reset ptrs if empty
}
 
// See if there is now enough room to restart writer
space = cbuf->len - cbuf->nb;
if (space >= cbuf->low_water) {
if (cbuf->waiting) {
cbuf->waiting = false;
cyg_drv_cond_broadcast(&cbuf->wait);
}
#ifdef CYGPKG_IO_SERIAL_SELECT_SUPPORT
cyg_selwakeup( &cbuf->selinfo );
#endif
}
 
#ifdef CYGDBG_USE_ASSERTS
cbuf->block_mode_xfer_running = false;
#endif
}
 
#endif // CYGINT_IO_SERIAL_BLOCK_TRANSFER
 
// ---------------------------------------------------------------------------
 
// EOF serial.c
/common/tty.c
0,0 → 1,336
//==========================================================================
//
// io/serial/common/tty.c
//
// TTY (terminal-like interface) I/O driver
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// 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
// Date: 1999-02-04
// Purpose: Device driver for tty I/O, layered on top of serial I/O
// Description:
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <pkgconf/io.h>
#include <pkgconf/io_serial.h>
#ifdef CYGPKG_IO_SERIAL_TTY
#include <cyg/io/io.h>
#include <cyg/io/devtab.h>
#include <cyg/io/ttyio.h>
#include <cyg/infra/diag.h>
 
static bool tty_init(struct cyg_devtab_entry *tab);
static Cyg_ErrNo tty_lookup(struct cyg_devtab_entry **tab,
struct cyg_devtab_entry *sub_tab,
const char *name);
static Cyg_ErrNo tty_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len);
static Cyg_ErrNo tty_read(cyg_io_handle_t handle, void *buf, cyg_uint32 *len);
static Cyg_ErrNo tty_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info);
static Cyg_ErrNo tty_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf, cyg_uint32 *len);
static Cyg_ErrNo tty_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len);
 
struct tty_private_info {
cyg_tty_info_t dev_info;
cyg_io_handle_t dev_handle;
};
 
static DEVIO_TABLE(tty_devio,
tty_write,
tty_read,
tty_select,
tty_get_config,
tty_set_config
);
 
#ifdef CYGPKG_IO_SERIAL_TTY_TTYDIAG
static struct tty_private_info tty_private_info_diag;
DEVTAB_ENTRY(tty_io_diag,
// "/dev/console",
// CYGDAT_IO_SERIAL_TTY_CONSOLE, // Based on driver for this device
"/dev/ttydiag",
"/dev/haldiag",
&tty_devio,
tty_init,
tty_lookup, // Execute this when device is being looked up
&tty_private_info_diag);
#endif
 
#ifdef CYGPKG_IO_SERIAL_TTY_TTY0
static struct tty_private_info tty_private_info0;
DEVTAB_ENTRY(tty_io0,
"/dev/tty0",
CYGDAT_IO_SERIAL_TTY_TTY0_DEV,
&tty_devio,
tty_init,
tty_lookup, // Execute this when device is being looked up
&tty_private_info0);
#endif
 
#ifdef CYGPKG_IO_SERIAL_TTY_TTY1
static struct tty_private_info tty_private_info1;
DEVTAB_ENTRY(tty_io1,
"/dev/tty1",
CYGDAT_IO_SERIAL_TTY_TTY1_DEV,
&tty_devio,
tty_init,
tty_lookup, // Execute this when device is being looked up
&tty_private_info1);
#endif
 
#ifdef CYGPKG_IO_SERIAL_TTY_TTY2
static struct tty_private_info tty_private_info2;
DEVTAB_ENTRY(tty_io2,
"/dev/tty2",
CYGDAT_IO_SERIAL_TTY_TTY2_DEV,
&tty_devio,
tty_init,
tty_lookup, // Execute this when device is being looked up
&tty_private_info2);
#endif
 
static bool
tty_init(struct cyg_devtab_entry *tab)
{
struct tty_private_info *priv = (struct tty_private_info *)tab->priv;
#ifdef CYGDBG_IO_INIT
diag_printf("Init tty channel: %x\n", tab);
#endif
priv->dev_info.tty_out_flags = CYG_TTY_OUT_FLAGS_DEFAULT;
priv->dev_info.tty_in_flags = CYG_TTY_IN_FLAGS_DEFAULT;
return true;
}
 
static Cyg_ErrNo
tty_lookup(struct cyg_devtab_entry **tab,
struct cyg_devtab_entry *sub_tab,
const char *name)
{
cyg_io_handle_t chan = (cyg_io_handle_t)sub_tab;
struct tty_private_info *priv = (struct tty_private_info *)(*tab)->priv;
#if 0
cyg_int32 len;
#endif
priv->dev_handle = chan;
#if 0
len = sizeof(cyg_serial_info_t);
// Initialize configuration
cyg_io_get_config(chan, CYG_SERIAL_GET_CONFIG,
(void *)&priv->dev_info.serial_config, &len);
#endif
return ENOERR;
}
 
#define BUFSIZE 64
 
static Cyg_ErrNo
tty_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
struct tty_private_info *priv = (struct tty_private_info *)t->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
cyg_int32 size, bytes_successful, actually_written;
cyg_uint8 xbuf[BUFSIZE];
cyg_uint8 c;
cyg_uint8 *buf = (cyg_uint8 *)_buf;
Cyg_ErrNo res = -EBADF;
// assert(chan)
size = 0;
bytes_successful = 0;
actually_written = 0;
while (bytes_successful++ < *len) {
c = *buf++;
if ((c == '\n') &&
(priv->dev_info.tty_out_flags & CYG_TTY_OUT_FLAGS_CRLF)) {
xbuf[size++] = '\r';
}
xbuf[size++] = c;
// Always leave room for possible CR/LF expansion
if ((size >= (BUFSIZE-1)) ||
(bytes_successful == *len)) {
res = cyg_io_write(chan, xbuf, &size);
if (res != ENOERR) {
*len = actually_written;
return res;
}
actually_written += size;
size = 0;
}
}
return res;
}
 
static Cyg_ErrNo
tty_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
struct tty_private_info *priv = (struct tty_private_info *)t->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
cyg_uint32 clen;
cyg_int32 size;
Cyg_ErrNo res;
cyg_uint8 c;
cyg_uint8 *buf = (cyg_uint8 *)_buf;
// assert(chan)
size = 0;
while (size < *len) {
clen = 1;
res = cyg_io_read(chan, &c, &clen);
if (res != ENOERR) {
*len = size;
return res;
}
buf[size++] = c;
if ((priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_BINARY) == 0) {
switch (c) {
case '\b': /* drop through */
case 0x7f:
size -= 2; // erase one character + 'backspace' char
if (size < 0) {
size = 0;
} else if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_ECHO) {
clen = 3;
cyg_io_write(chan, "\b \b", &clen);
}
break;
case '\r':
if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_CRLF) {
/* Don't do anything because a '\n' will come next */
break;
}
if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_CR) {
c = '\n'; // Map CR -> LF
}
/* drop through */
case '\n':
if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_ECHO) {
if (priv->dev_info.tty_out_flags & CYG_TTY_OUT_FLAGS_CRLF) {
clen = 2;
cyg_io_write(chan, "\r\n", &clen);
} else {
clen = 1;
cyg_io_write(chan, &c, &clen);
}
}
buf[size-1] = c;
*len = size;
return ENOERR;
default:
if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_ECHO) {
clen = 1;
cyg_io_write(chan, &c, &clen);
}
break;
}
}
}
*len = size;
return ENOERR;
}
 
static cyg_bool
tty_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
struct tty_private_info *priv = (struct tty_private_info *)t->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
 
// Just pass it on to next driver level
return cyg_io_select( chan, which, info );
}
 
static Cyg_ErrNo
tty_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf, cyg_uint32 *len)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
struct tty_private_info *priv = (struct tty_private_info *)t->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
Cyg_ErrNo res = ENOERR;
#if 0
cyg_int32 current_len;
#endif
// assert(chan)
switch (key) {
case CYG_IO_GET_CONFIG_TTY_INFO:
if (*len < sizeof(cyg_tty_info_t)) {
return -EINVAL;
}
#if 0
current_len = sizeof(cyg_serial_info_t);
res = cyg_io_get_config(chan, CYG_SERIAL_GET_CONFIG,
(void *)&priv->dev_info.serial_config, &current_len);
if (res != ENOERR) {
return res;
}
#endif
*(cyg_tty_info_t *)buf = priv->dev_info;
*len = sizeof(cyg_tty_info_t);
break;
default: // Assume this is a 'serial' driver control
res = cyg_io_get_config(chan, key, buf, len);
}
return res;
}
 
static Cyg_ErrNo
tty_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
struct tty_private_info *priv = (struct tty_private_info *)t->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
#if 0
cyg_int32 current_len;
#endif
Cyg_ErrNo res = ENOERR;
// assert(chan)
switch (key) {
case CYG_IO_SET_CONFIG_TTY_INFO:
if (*len != sizeof(cyg_tty_info_t)) {
return -EINVAL;
}
priv->dev_info = *(cyg_tty_info_t *)buf;
break;
default: // Pass on to serial driver
res = cyg_io_set_config(chan, key, buf, len);
}
return res;
}
#endif // CYGPKG_IO_SERIAL_TTY
/common/termios.c
0,0 → 1,402
/* ====================================================================
//
// termios.c
//
// POSIX termios API implementation
//
// ====================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// 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): jlarmour
// Contributors:
// Date: 2000-07-22
// Purpose: POSIX termios API support
// Description: Most of the real work happens in the POSIX termios tty
// drivers
//
//####DESCRIPTIONEND####
//
// ==================================================================*/
 
// CONFIGURATION
 
#include <pkgconf/io_serial.h>
 
#ifdef CYGPKG_IO_SERIAL_TERMIOS
 
// INCLUDES
 
#include <termios.h> // Header for this file
#include <cyg/infra/cyg_type.h> // Common stuff
#include <cyg/infra/cyg_ass.h> // Assertion support
#include <cyg/infra/cyg_trac.h> // Tracing support
#include <cyg/io/serialio.h> // eCos serial implementation
#include <cyg/fileio/fileio.h> // file operations
#include <cyg/io/io.h>
#include <errno.h> // errno
#include <unistd.h> // isatty()
 
// TYPES
 
typedef struct {
const struct termios *termios_p;
int optact;
} setattr_struct;
 
// FUNCTIONS
 
extern speed_t
cfgetospeed( const struct termios *termios_p )
{
CYG_REPORT_FUNCTYPE( "returning speed code %d" );
CYG_CHECK_DATA_PTRC( termios_p );
CYG_REPORT_FUNCARG1XV( termios_p );
CYG_REPORT_RETVAL( termios_p->c_ospeed );
return termios_p->c_ospeed;
} // cfgetospeed()
 
 
extern int
cfsetospeed( struct termios *termios_p, speed_t speed )
{
CYG_REPORT_FUNCTYPE( "returning %d" );
CYG_CHECK_DATA_PTRC( termios_p );
CYG_REPORT_FUNCARG2( "termios_p=%08x, speed=%d", termios_p, speed );
CYG_REPORT_RETVAL( termios_p->c_ospeed );
if ( speed > B4000000 ) {
errno = EINVAL;
CYG_REPORT_RETVAL( -1 );
return -1;
}
termios_p->c_ospeed = speed;
CYG_REPORT_RETVAL( 0 );
return 0;
} // cfsetospeed()
 
 
extern speed_t
cfgetispeed( const struct termios *termios_p )
{
CYG_REPORT_FUNCTYPE( "returning speed code %d" );
CYG_CHECK_DATA_PTRC( termios_p );
CYG_REPORT_FUNCARG1XV( termios_p );
CYG_REPORT_RETVAL( termios_p->c_ispeed );
return termios_p->c_ispeed;
} // cfgetispeed()
 
 
extern int
cfsetispeed( struct termios *termios_p, speed_t speed )
{
CYG_REPORT_FUNCTYPE( "returning %d" );
CYG_CHECK_DATA_PTRC( termios_p );
CYG_REPORT_FUNCARG2( "termios_p=%08x, speed=%d", termios_p, speed );
if ( speed > B115200 ) {
errno = EINVAL;
CYG_REPORT_RETVAL( -1 );
return -1;
}
termios_p->c_ispeed = speed;
CYG_REPORT_RETVAL( 0 );
return 0;
} // cfsetispeed()
 
 
__externC cyg_file *
cyg_fp_get( int fd );
 
__externC void
cyg_fp_free( cyg_file *fp );
 
extern int
tcgetattr( int fildes, struct termios *termios_p )
{
cyg_file *fp;
int ret;
int len = sizeof( *termios_p );
 
CYG_REPORT_FUNCTYPE( "returning %d" );
CYG_REPORT_FUNCARG2( "fildes=%d, termios_p=%08x", fildes, termios_p );
CYG_CHECK_DATA_PTRC( termios_p );
 
if ( !isatty(fildes) ) {
errno = ENOTTY;
CYG_REPORT_RETVAL( -1 );
return -1;
}
fp = cyg_fp_get( fildes );
 
if ( NULL == fp ) {
errno = EBADF;
CYG_REPORT_RETVAL( -1 );
return -1;
}
 
ret = fp->f_ops->fo_getinfo( fp, CYG_IO_GET_CONFIG_TERMIOS, termios_p,
len);
cyg_fp_free( fp );
 
if ( ret > 0 ) {
errno = ret;
CYG_REPORT_RETVAL( -1 );
return -1;
}
CYG_REPORT_RETVAL( 0 );
return 0;
} // tcgetattr()
 
 
extern int
tcsetattr( int fildes, int optact, const struct termios *termios_p )
{
cyg_file *fp;
int ret;
setattr_struct attr;
int len = sizeof( attr );
 
CYG_REPORT_FUNCTYPE( "returning %d" );
CYG_REPORT_FUNCARG3( "fildes=%d, optact=%d, termios_p=%08x",
fildes, optact, termios_p );
CYG_CHECK_DATA_PTRC( termios_p );
 
if ( !isatty(fildes) ) {
errno = ENOTTY;
CYG_REPORT_RETVAL( -1 );
return -1;
}
 
if ( (optact != TCSANOW) && (optact != TCSADRAIN) &&
(optact != TCSAFLUSH) ) {
errno = EINVAL;
CYG_REPORT_RETVAL( -1 );
return -1;
}
fp = cyg_fp_get( fildes );
 
if ( NULL == fp ) {
errno = EBADF;
CYG_REPORT_RETVAL( -1 );
return -1;
}
 
attr.termios_p = termios_p;
attr.optact = optact;
 
ret = fp->f_ops->fo_setinfo( fp, CYG_IO_SET_CONFIG_TERMIOS, &attr,
len);
 
cyg_fp_free( fp );
 
if ( ret > 0 ) {
errno = ret;
CYG_REPORT_RETVAL( -1 );
return -1;
}
CYG_REPORT_RETVAL( 0 );
return 0;
} // tcsetattr()
 
 
extern int
tcsendbreak( int fildes, int duration )
{
// FIXME
return -EINVAL;
} // tcsendbreak()
 
extern int
tcdrain( int fildes )
{
cyg_file *fp;
int ret;
 
CYG_REPORT_FUNCTYPE( "returning %d" );
CYG_REPORT_FUNCARG1DV( fildes );
 
if ( !isatty(fildes) ) {
errno = ENOTTY;
CYG_REPORT_RETVAL( -1 );
return -1;
}
 
fp = cyg_fp_get( fildes );
 
if ( NULL == fp ) {
errno = EBADF;
CYG_REPORT_RETVAL( -1 );
return -1;
}
 
ret = fp->f_ops->fo_getinfo( fp,
CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN,
NULL, 0 );
cyg_fp_free( fp );
 
if ( ret > 0 ) {
errno = ret;
CYG_REPORT_RETVAL( -1 );
return -1;
}
CYG_REPORT_RETVAL( 0 );
return 0;
} // tcdrain()
 
extern int
tcflush( int fildes, int queue_sel )
{
cyg_file *fp;
int ret;
 
CYG_REPORT_FUNCTYPE( "returning %d" );
CYG_REPORT_FUNCARG2DV( fildes, queue_sel );
 
if ( !isatty(fildes) ) {
errno = ENOTTY;
CYG_REPORT_RETVAL( -1 );
return -1;
}
 
fp = cyg_fp_get( fildes );
 
if ( NULL == fp ) {
errno = EBADF;
CYG_REPORT_RETVAL( -1 );
return -1;
}
 
switch( queue_sel ) {
case TCIOFLUSH:
ret = fp->f_ops->fo_getinfo( fp,
CYG_IO_GET_CONFIG_SERIAL_OUTPUT_FLUSH,
NULL, 0 );
// fallthrough
case TCIFLUSH:
ret = fp->f_ops->fo_getinfo( fp,
CYG_IO_GET_CONFIG_SERIAL_INPUT_FLUSH,
NULL, 0 );
break;
case TCOFLUSH:
ret = fp->f_ops->fo_getinfo( fp,
CYG_IO_GET_CONFIG_SERIAL_OUTPUT_FLUSH,
NULL, 0 );
break;
default:
ret = EINVAL;
break;
}
 
cyg_fp_free( fp );
 
if ( ret > 0 ) {
errno = ret;
CYG_REPORT_RETVAL( -1 );
return -1;
}
CYG_REPORT_RETVAL( 0 );
return 0;
} // tcflush()
 
extern int
tcflow( int fildes, int action )
{
cyg_file *fp;
int ret;
cyg_uint32 forcearg;
int len = sizeof(forcearg);
 
CYG_REPORT_FUNCTYPE( "returning %d" );
CYG_REPORT_FUNCARG2DV( fildes, action );
 
if ( !isatty(fildes) ) {
errno = ENOTTY;
CYG_REPORT_RETVAL( -1 );
return -1;
}
 
fp = cyg_fp_get( fildes );
 
if ( NULL == fp ) {
errno = EBADF;
CYG_REPORT_RETVAL( -1 );
return -1;
}
 
switch( action ) {
case TCOOFF:
forcearg = CYGNUM_SERIAL_FLOW_THROTTLE_TX;
ret = fp->f_ops->fo_setinfo( fp,
CYG_IO_SET_CONFIG_SERIAL_FLOW_CONTROL_FORCE,
&forcearg, len );
break;
case TCOON:
forcearg = CYGNUM_SERIAL_FLOW_RESTART_TX;
ret = fp->f_ops->fo_setinfo( fp,
CYG_IO_SET_CONFIG_SERIAL_FLOW_CONTROL_FORCE,
&forcearg, len );
break;
case TCIOFF:
forcearg = CYGNUM_SERIAL_FLOW_THROTTLE_RX;
ret = fp->f_ops->fo_setinfo( fp,
CYG_IO_SET_CONFIG_SERIAL_FLOW_CONTROL_FORCE,
&forcearg, len );
break;
case TCION:
forcearg = CYGNUM_SERIAL_FLOW_RESTART_RX;
ret = fp->f_ops->fo_setinfo( fp,
CYG_IO_SET_CONFIG_SERIAL_FLOW_CONTROL_FORCE,
&forcearg, len );
break;
default:
ret = EINVAL;
break;
}
 
cyg_fp_free( fp );
 
if ( ret > 0 ) {
errno = ret;
CYG_REPORT_RETVAL( -1 );
return -1;
}
CYG_REPORT_RETVAL( 0 );
return 0;
} // tcflow()
 
#endif // ifdef CYGPKG_IO_SERIAL_TERMIOS
 
// EOF termios.c
/common/termiostty.c
0,0 → 1,902
//==========================================================================
//
// termiostty.c
//
// POSIX Termios compatible TTY I/O 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 Jonathan Larmour
//
// 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): jlarmour
// Contributors: gthomas
// Date: 2000-07-22
// Purpose: Device driver for termios emulation tty I/O, layered on
// top of serial I/O
// Description: TODO: Add OPOST support for 80x25 (configurable) windows
// TODO: Support _POSIX_VDISABLE
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
// CONFIGURATION
 
#include <pkgconf/io_serial.h>
 
#ifdef CYGPKG_IO_SERIAL_TERMIOS
 
// INCLUDES
 
#include <cyg/infra/cyg_type.h> // Common types
#include <cyg/infra/cyg_ass.h> // Assertion support
#include <cyg/infra/cyg_trac.h> // Tracing support
#include <cyg/io/io.h>
#include <cyg/io/devtab.h>
#include <cyg/io/serialio.h> // public serial API
#include <termios.h> // Termios header
#include <cyg/hal/drv_api.h>
#include <stdlib.h> // malloc
#include <string.h>
#ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
# include <signal.h>
#endif
 
//==========================================================================
// FUNCTION PROTOTYPES
 
static bool
termios_init(struct cyg_devtab_entry *tab);
 
static Cyg_ErrNo
termios_lookup(struct cyg_devtab_entry **tab,
struct cyg_devtab_entry *sub_tab,
const char *name);
static Cyg_ErrNo
termios_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len);
static Cyg_ErrNo
termios_read(cyg_io_handle_t handle, void *buf, cyg_uint32 *len);
static Cyg_ErrNo
termios_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info);
static Cyg_ErrNo
termios_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf,
cyg_uint32 *len);
static Cyg_ErrNo
termios_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf,
cyg_uint32 *len);
 
//==========================================================================
// TYPE DEFINITIONS
 
struct termios_private_info {
struct termios termios;
cyg_io_handle_t dev_handle;
cyg_drv_mutex_t lock;
cyg_bool init;
cyg_uint8 *errbuf;
cyg_uint8 *errbufpos;
cyg_uint32 errbufsize;
};
 
typedef struct {
struct termios *termios_p;
int optact;
} setattr_struct;
 
 
//==========================================================================
// STATIC OBJECTS
 
static DEVIO_TABLE(termios_devio,
termios_write,
termios_read,
termios_select,
termios_get_config,
termios_set_config);
 
#ifdef CYGPKG_IO_SERIAL_TERMIOS_TERMIOS0
static struct termios_private_info termios_private_info0;
DEVTAB_ENTRY(termios_io0,
"/dev/termios0",
CYGDAT_IO_SERIAL_TERMIOS_TERMIOS0_DEV,
&termios_devio,
termios_init,
termios_lookup,
&termios_private_info0);
#endif
 
#ifdef CYGPKG_IO_SERIAL_TERMIOS_TERMIOS1
static struct termios_private_info termios_private_info1;
DEVTAB_ENTRY(termios_io1,
"/dev/termios1",
CYGDAT_IO_SERIAL_TERMIOS_TERMIOS1_DEV,
&termios_devio,
termios_init,
termios_lookup,
&termios_private_info1);
#endif
 
#ifdef CYGPKG_IO_SERIAL_TERMIOS_TERMIOS2
static struct termios_private_info termios_private_info2;
DEVTAB_ENTRY(termios_io2,
"/dev/termios2",
CYGDAT_IO_SERIAL_TERMIOS_TERMIOS2_DEV,
&termios_devio,
termios_init,
termios_lookup,
&termios_private_info2);
#endif
 
static const cc_t c_cc_init[ NCCS ] = {
0x04, /* EOF == ^D */
0, /* EOL */
0x08, /* ERASE = BS ; NB DEL=0x7f */
0x03, /* INTR = ^C */
0x15, /* KILL = ^U */
0, /* MIN = 0 */
0x1c, /* QUIT = ^\ */
0x1a, /* SUSP = ^Z ; NB ignored in this impl - no job control */
0, /* TIME = 0 */
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
CYGDAT_IO_SERIAL_FLOW_CONTROL_XON_CHAR,
CYGDAT_IO_SERIAL_FLOW_CONTROL_XOFF_CHAR,
#else
17,
19,
#endif
};
 
// map eCos bitrates to POSIX bitrates.
static speed_t ecosbaud2posixbaud[] = {
0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B2400, B3600,
B4800, B7200, B9600, B14400, B19200, B38400, B57600, B115200, B230400 };
 
// map POSIX bitrates to eCos bitrates.
static cyg_serial_baud_rate_t posixbaud2ecosbaud[] = {
0, CYGNUM_SERIAL_BAUD_50, CYGNUM_SERIAL_BAUD_75, CYGNUM_SERIAL_BAUD_110,
CYGNUM_SERIAL_BAUD_134_5, CYGNUM_SERIAL_BAUD_150, CYGNUM_SERIAL_BAUD_200,
CYGNUM_SERIAL_BAUD_300, CYGNUM_SERIAL_BAUD_600, CYGNUM_SERIAL_BAUD_1200,
CYGNUM_SERIAL_BAUD_1800, CYGNUM_SERIAL_BAUD_2400, CYGNUM_SERIAL_BAUD_3600,
CYGNUM_SERIAL_BAUD_4800, CYGNUM_SERIAL_BAUD_7200, CYGNUM_SERIAL_BAUD_9600,
CYGNUM_SERIAL_BAUD_14400, CYGNUM_SERIAL_BAUD_19200,
CYGNUM_SERIAL_BAUD_38400, CYGNUM_SERIAL_BAUD_57600,
CYGNUM_SERIAL_BAUD_115200, CYGNUM_SERIAL_BAUD_230400 };
 
 
//==========================================================================
// FUNCTIONS
 
static __inline__ speed_t
map_ecosbaud_to_posixbaud( cyg_serial_baud_rate_t ebaud )
{
if ( ebaud > (sizeof(ecosbaud2posixbaud) / sizeof(speed_t)) )
return 0;
return ecosbaud2posixbaud[ ebaud ];
}
 
static __inline__ cyg_serial_baud_rate_t
map_posixbaud_to_ecosbaud( speed_t pbaud )
{
if ( pbaud > (sizeof(posixbaud2ecosbaud)/sizeof(cyg_serial_baud_rate_t)) )
return 0;
return posixbaud2ecosbaud[ pbaud ];
}
 
//==========================================================================
// real_termios_init is used to initialize the termios structure. This is
// called at lookup time, and not from termios_init() because it needs
// to query the serial device which may not be set up yet at that point
// in termios_init()
 
#ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
# define C_IFLAG_INIT (ICRNL|IGNBRK|BRKINT)
#else
# define C_IFLAG_INIT (ICRNL|IGNBRK)
#endif
#define C_OFLAG_INIT (ONLCR)
#define C_CFLAG_INIT (CREAD)
#define C_LFLAG_INIT (ECHO|ECHOE|ECHOK|ICANON)
 
static Cyg_ErrNo
real_termios_init( struct termios_private_info *priv )
{
Cyg_ErrNo res;
struct termios *t;
cyg_serial_info_t dev_conf;
cyg_serial_buf_info_t dev_buf_conf;
cyg_uint32 len = sizeof( dev_conf );
 
CYG_REPORT_FUNCTYPE("returning %d");
CYG_REPORT_FUNCARG1XV( priv );
CYG_CHECK_DATA_PTRC( priv );
 
t = &priv->termios;
 
// Get info from driver
res = cyg_io_get_config( priv->dev_handle, CYG_IO_GET_CONFIG_SERIAL_INFO,
&dev_conf, &len );
if ( ENOERR == res ) {
len = sizeof( dev_buf_conf );
res = cyg_io_get_config( priv->dev_handle,
CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO,
&dev_buf_conf, &len );
}
 
priv->errbuf = (cyg_uint8 *)malloc( dev_buf_conf.rx_bufsize );
if ( NULL == priv->errbuf )
res = ENOMEM; // FIXME: Are we allowed to do this?
priv->errbufpos = priv->errbuf;
priv->errbufsize = dev_buf_conf.rx_bufsize;
 
if ( ENOERR != res ) {
CYG_REPORT_RETVAL( res );
return res;
}
// we only support symmetric baud rates
t->c_ispeed = t->c_ospeed = map_ecosbaud_to_posixbaud( dev_conf.baud );
t->c_iflag = C_IFLAG_INIT;
t->c_oflag = C_OFLAG_INIT;
t->c_cflag = C_CFLAG_INIT;
t->c_lflag = C_LFLAG_INIT;
memcpy( t->c_cc, c_cc_init, sizeof( t->c_cc ) );
switch ( dev_conf.parity ) {
case CYGNUM_SERIAL_PARITY_NONE:
t->c_iflag |= IGNPAR;
break;
case CYGNUM_SERIAL_PARITY_ODD:
t->c_cflag |= PARODD;
// DROPTHROUGH
case CYGNUM_SERIAL_PARITY_EVEN:
t->c_iflag |= PARENB;
break;
default:
CYG_FAIL( "Unsupported default parity" );
break;
}
 
switch( dev_conf.word_length ) {
case CYGNUM_SERIAL_WORD_LENGTH_5:
t->c_cflag |= CS5;
break;
case CYGNUM_SERIAL_WORD_LENGTH_6:
t->c_cflag |= CS6;
break;
case CYGNUM_SERIAL_WORD_LENGTH_7:
t->c_cflag |= CS7;
break;
case CYGNUM_SERIAL_WORD_LENGTH_8:
t->c_cflag |= CS8;
break;
default:
CYG_FAIL( "Unsupported word length" );
break;
}
 
switch ( dev_conf.stop ) {
case CYGNUM_SERIAL_STOP_1:
// Don't need to do anything
break;
case CYGNUM_SERIAL_STOP_2:
t->c_cflag |= CSTOPB;
break;
default:
CYG_FAIL( "Unsupported number of stop bits" );
break;
}
 
switch ( dev_conf.flags ) {
case CYGNUM_SERIAL_FLOW_RTSCTS_RX:
t->c_cflag |= CRTSCTS;
// drop through
case CYGNUM_SERIAL_FLOW_XONXOFF_RX:
t->c_iflag |= IXOFF;
break;
case CYGNUM_SERIAL_FLOW_RTSCTS_TX:
t->c_cflag |= CRTSCTS;
// drop through
case CYGNUM_SERIAL_FLOW_XONXOFF_TX:
t->c_iflag |= IXON;
break;
default:
// Ignore flags we don't grok
break;
}
 
return ENOERR;
} // real_termios_init()
 
//==========================================================================
// set_attr() actually enacts the termios config. We come in here with
// the mutex in priv locked
//
// Possible deviation from standard: POSIX says we should enact which ever
// bits we can and only return EINVAL when none of them can be performed
// Rather than tracking whether *none* of them worked, instead we just
// always claim success. At the very least, setting c_cc is never to
// fail so I'm not sure if this is really non-standard or not!
 
static Cyg_ErrNo
set_attr( struct termios *t, struct termios_private_info *priv )
{
Cyg_ErrNo res = ENOERR;
cyg_serial_info_t dev_conf, new_dev_conf;
cyg_uint32 len = sizeof( dev_conf );
cc_t *tempcc = &priv->termios.c_cc[0];
struct termios *ptermios = &priv->termios;
// Get info from driver
res = cyg_io_get_config( priv->dev_handle, CYG_IO_GET_CONFIG_SERIAL_INFO,
&dev_conf, &len );
 
if ( ENOERR != res )
return res;
// We need to set up each facet of config to change one by one because
// POSIX says we should try and change as much of the config as possible
// This is tedious and has to be done by steam :-(
 
if ( t->c_ospeed != ptermios->c_ospeed ) {
new_dev_conf = dev_conf;
new_dev_conf.baud = map_posixbaud_to_ecosbaud( t->c_ospeed );
if ( 0 != new_dev_conf.baud ) {
len = sizeof( new_dev_conf );
res = cyg_io_set_config( priv->dev_handle,
CYG_IO_SET_CONFIG_SERIAL_INFO,
&new_dev_conf, &len );
if ( ENOERR == res ) {
// It worked, so update dev_conf to reflect the new state
dev_conf.baud = new_dev_conf.baud;
// and termios
ptermios->c_ispeed = t->c_ospeed;
ptermios->c_ospeed = t->c_ospeed;
}
}
}
 
if ( (t->c_cflag & CSTOPB) != (ptermios->c_cflag & CSTOPB) ) {
new_dev_conf = dev_conf;
if ( t->c_cflag & CSTOPB )
new_dev_conf.stop = CYGNUM_SERIAL_STOP_2;
else
new_dev_conf.stop = CYGNUM_SERIAL_STOP_1;
len = sizeof( new_dev_conf );
res = cyg_io_set_config( priv->dev_handle,
CYG_IO_SET_CONFIG_SERIAL_INFO,
&new_dev_conf, &len );
if ( ENOERR == res ) {
// It worked, so update dev_conf to reflect the new state
dev_conf.stop = new_dev_conf.stop;
// and termios
ptermios->c_cflag &= ~CSTOPB;
ptermios->c_cflag |= t->c_cflag & CSTOPB;
}
}
 
if ( ((t->c_cflag & PARENB) != (ptermios->c_cflag & PARENB)) ||
((t->c_cflag & PARODD) != (ptermios->c_cflag & PARODD)) ) {
new_dev_conf = dev_conf;
if ( t->c_cflag & PARENB )
if ( t->c_cflag & PARODD )
new_dev_conf.parity = CYGNUM_SERIAL_PARITY_ODD;
else
new_dev_conf.parity = CYGNUM_SERIAL_PARITY_EVEN;
else
new_dev_conf.parity = CYGNUM_SERIAL_PARITY_NONE;
len = sizeof( new_dev_conf );
res = cyg_io_set_config( priv->dev_handle,
CYG_IO_SET_CONFIG_SERIAL_INFO,
&new_dev_conf, &len );
if ( ENOERR == res ) {
// It worked, so update dev_conf to reflect the new state
dev_conf.parity = new_dev_conf.parity;
// and termios
ptermios->c_cflag &= ~(PARENB|PARODD);
ptermios->c_cflag |= t->c_cflag & (PARENB|PARODD);
}
}
 
if ( (t->c_cflag & CSIZE) != (ptermios->c_cflag & CSIZE) ) {
new_dev_conf = dev_conf;
switch ( t->c_cflag & CSIZE ) {
case CS5:
new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_5;
break;
case CS6:
new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_6;
break;
case CS7:
new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_7;
break;
case CS8:
new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_8;
break;
}
len = sizeof( new_dev_conf );
res = cyg_io_set_config( priv->dev_handle,
CYG_IO_SET_CONFIG_SERIAL_INFO,
&new_dev_conf, &len );
if ( ENOERR == res ) {
// It worked, so update dev_conf to reflect the new state
dev_conf.word_length = new_dev_conf.word_length;
// and termios
ptermios->c_cflag &= ~CSIZE;
ptermios->c_cflag |= t->c_cflag & CSIZE;
}
}
 
if ( (t->c_cflag & IXOFF) != (ptermios->c_cflag & IXOFF) ) {
new_dev_conf = dev_conf;
new_dev_conf.flags &=
~(CYGNUM_SERIAL_FLOW_XONXOFF_RX|CYGNUM_SERIAL_FLOW_RTSCTS_RX);
if ( t->c_cflag & IXOFF )
if ( t->c_cflag & CRTSCTS)
new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_RTSCTS_RX;
else
new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_XONXOFF_RX;
else
new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_NONE;
len = sizeof( new_dev_conf );
res = cyg_io_set_config( priv->dev_handle,
CYG_IO_SET_CONFIG_SERIAL_INFO,
&new_dev_conf, &len );
if ( ENOERR == res ) {
// It worked, so update dev_conf to reflect the new state
dev_conf.flags = new_dev_conf.flags;
// and termios
ptermios->c_cflag &= ~(IXOFF|CRTSCTS);
ptermios->c_cflag |= t->c_cflag & (IXOFF|CRTSCTS);
}
}
 
if ( (t->c_cflag & IXON) != (ptermios->c_cflag & IXON) ) {
new_dev_conf = dev_conf;
new_dev_conf.flags &=
~(CYGNUM_SERIAL_FLOW_XONXOFF_TX|CYGNUM_SERIAL_FLOW_RTSCTS_TX);
if ( t->c_cflag & IXON )
if ( t->c_cflag & CRTSCTS)
new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_RTSCTS_TX;
else
new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_XONXOFF_TX;
else
new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_NONE;
len = sizeof( new_dev_conf );
res = cyg_io_set_config( priv->dev_handle,
CYG_IO_SET_CONFIG_SERIAL_INFO,
&new_dev_conf, &len );
if ( ENOERR == res ) {
// It worked, so update dev_conf to reflect the new state
dev_conf.flags = new_dev_conf.flags;
// and termios
ptermios->c_cflag &= ~(IXON|CRTSCTS);
ptermios->c_cflag |= t->c_cflag & (IXON|CRTSCTS);
}
}
 
// Input/Output processing flags can just be set as we grok them all
// with few exceptions (see lflags below)
ptermios->c_iflag &= ~(BRKINT|ICRNL|IGNBRK|IGNCR|IGNPAR|INLCR|INPCK|
ISTRIP|PARMRK);
ptermios->c_iflag |= t->c_iflag & (
#ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
BRKINT|
#endif
ICRNL|IGNBRK|IGNCR|IGNPAR|
INLCR|INPCK|ISTRIP|PARMRK );
ptermios->c_oflag &= ~(OPOST|ONLCR);
ptermios->c_oflag |= t->c_oflag & (OPOST|ONLCR);
 
ptermios->c_cflag &= ~(CLOCAL|CREAD|HUPCL);
ptermios->c_cflag |= t->c_cflag & (CLOCAL|CREAD|HUPCL);
 
ptermios->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|
IEXTEN|ISIG|NOFLSH|TOSTOP);
// Note we don't support IEXTEN nor TOSTOP so we don't set them
ptermios->c_lflag |= t->c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ICANON|
#ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
ISIG|
#endif
NOFLSH);
 
// control characters. We don't support changing of VSTART, VSTOP,
// VTIME or VSUSP though
tempcc[VEOF] = t->c_cc[VEOF];
tempcc[VEOL] = t->c_cc[VEOL];
tempcc[VERASE] = t->c_cc[VERASE];
tempcc[VINTR] = t->c_cc[VINTR];
tempcc[VKILL] = t->c_cc[VKILL];
tempcc[VMIN] = t->c_cc[VMIN];
tempcc[VQUIT] = t->c_cc[VQUIT];
return res;
}
 
//==========================================================================
 
static bool
termios_init(struct cyg_devtab_entry *tab)
{
// can't initialize the termios structure because we can't
// query the serial driver yet. Wait until lookup time.
 
return true;
} // termios_init()
 
//==========================================================================
 
static Cyg_ErrNo
termios_lookup(struct cyg_devtab_entry **tab,
struct cyg_devtab_entry *sub_tab,
const char *name)
{
cyg_io_handle_t chan = (cyg_io_handle_t)sub_tab;
struct termios_private_info *priv =
(struct termios_private_info *)(*tab)->priv;
Cyg_ErrNo err = ENOERR;
if ( !priv->init ) {
cyg_drv_mutex_lock( &priv->lock );
if ( !priv->init ) { // retest as we may have been pre-empted
priv->dev_handle = chan;
err = real_termios_init( priv );
}
cyg_drv_mutex_unlock( &priv->lock );
}
return err;
}
 
//==========================================================================
 
#define WRITE_BUFSIZE 100 // FIXME: ->CDL
// #define MAX_CANON 64 FIXME: relevance?
 
 
static Cyg_ErrNo
termios_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
struct termios_private_info *priv = (struct termios_private_info *)t->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
cyg_int32 xbufsize, input_bytes_read;
cyg_uint8 xbuf[WRITE_BUFSIZE];
cyg_uint8 *buf = (cyg_uint8 *)_buf;
Cyg_ErrNo res;
 
xbufsize = input_bytes_read = 0;
while (input_bytes_read++ < *len) {
if ( (*buf == '\n') && (priv->termios.c_oflag & (OPOST|ONLCR)) ) {
xbuf[xbufsize++] = '\r';
}
xbuf[xbufsize++] = *buf;
if ((xbufsize >= (WRITE_BUFSIZE-1)) || (input_bytes_read == *len) ||
(*buf == '\n'))
{
cyg_int32 size = xbufsize;
res = cyg_io_write(chan, xbuf, &size);
if (res != ENOERR) {
*len = input_bytes_read - (xbufsize - size);
return res;
}
xbufsize = 0;
}
buf++;
}
// Everything sent, so *len is correct.
return ENOERR;
}
 
 
//==========================================================================
 
static Cyg_ErrNo
termios_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len)
{
cyg_devtab_entry_t *dt = (cyg_devtab_entry_t *)handle;
struct termios_private_info *priv = (struct termios_private_info *)dt->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
struct termios *t = &priv->termios;
cyg_uint32 clen;
cyg_uint32 size;
Cyg_ErrNo res;
cyg_uint8 c;
cyg_uint8 *buf = (cyg_uint8 *)_buf;
cyg_bool discardc; // should c be discarded (not read, not printed)
cyg_bool returnnow = false; // return back to user after this char
// if receiver off
if (0 == (t->c_cflag & CREAD) ) {
*len = 0;
return -EINVAL;
}
 
size = 0;
if ( 0 == (t->c_lflag & ICANON) ) {
// In non-canonical mode we return the min of *len and the
// number of bytes available
// So we query the driver for how many bytes are available - this
// guarantees we won't block
cyg_serial_buf_info_t dev_buf_conf;
cyg_uint32 dbc_len = sizeof( dev_buf_conf );
res = cyg_io_get_config( chan,
CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO,
&dev_buf_conf, &dbc_len );
CYG_ASSERT( res == ENOERR, "Query buffer status failed!" );
*len = *len < dev_buf_conf.rx_count ? *len : dev_buf_conf.rx_count;
} // if
 
while (!returnnow && size < *len) {
clen = 1;
discardc = false;
res = cyg_io_read(chan, &c, &clen);
if (res != ENOERR) {
*len = size;
return res;
}
 
// lock to prevent termios getting corrupted while we read from it
cyg_drv_mutex_lock( &priv->lock );
 
if ( t->c_iflag & ISTRIP )
c &= 0x7f;
 
// canonical mode: erase, kill, and newline processing
if ( t->c_lflag & ICANON ) {
if ( t->c_cc[ VERASE ] == c ) {
discardc = true;
// erase on display?
if ( (t->c_lflag & ECHO) && (t->c_lflag & ECHOE) ) {
cyg_uint8 erasebuf[3];
erasebuf[0] = erasebuf[2] = t->c_cc[ VERASE ];
erasebuf[1] = ' ';
clen = sizeof(erasebuf);
// FIXME: what about error or non-blocking?
cyg_io_write(chan, erasebuf, &clen);
}
if ( size )
size--;
} // if
else if ( t->c_cc[ VKILL ] == c ) {
// kill line on display?
if ( (t->c_lflag & ECHO) && (t->c_lflag & ECHOK) ) {
 
// we could try and be more efficient here and
// output a stream of erases, and then a stream
// of spaces and then more erases. But this is poor
// because on a slow terminal the user will see characters
// delete from the middle forward in chunks!
// But at least we try and chunk up sets of writes
cyg_uint8 erasebuf[30];
cyg_uint8 erasechunks;
cyg_uint8 i;
 
erasechunks = size < (sizeof(erasebuf)/3) ?
size : (sizeof(erasebuf)/3);
 
for (i=0; i<erasechunks; i++) {
erasebuf[i*3] = erasebuf[i*3+2] = t->c_cc[ VERASE ];
erasebuf[i*3+1] = ' ';
}
 
while( size ) {
cyg_uint8 j;
 
j = size < (sizeof(erasebuf)/3) ?
size : (sizeof(erasebuf)/3);
clen = (j*3);
// FIXME: what about error or non-blocking?
cyg_io_write( chan, erasebuf, &clen );
size -= j;
}
} else
size = 0;
discardc = true;
} // else if
// CR
else if ( '\r' == c ) {
if ( t->c_iflag & IGNCR )
discardc = true;
else if ( t->c_iflag & ICRNL )
c = '\n';
}
// newlines or similar.
// Note: not an else if to catch CRNL conversion
if ( (t->c_cc[ VEOF ] == c) || (t->c_cc[ VEOL ] == c) ||
('\n' == c) ) {
if ( t->c_cc[ VEOF ] == c )
discardc = true;
if ( t->c_lflag & ECHONL ) { // don't check ECHO in this case
clen = 1;
// FIXME: what about error or non-blocking?
// FIXME: what if INLCR is set?
cyg_io_write( chan, "\n", &clen );
}
if ( t->c_iflag & INLCR )
c = '\r';
returnnow = true; // FIXME: true even for INLCR?
} // else if
} else { // non-canonical mode
if ( t->c_cc[ VMIN ] && (size+1 >= t->c_cc[ VMIN ]) )
returnnow = true;
} // else
 
#ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
if ( (t->c_lflag & ISIG) && (t->c_cc[ VINTR ] == c) ) {
discardc = true;
if ( 0 == (t->c_lflag & NOFLSH) )
size = 0;
// raise could be a non-local jump - we should unlock mutex
cyg_drv_mutex_unlock( &priv->lock );
 
// FIXME: what if raise returns != 0?
raise( SIGINT );
cyg_drv_mutex_lock( &priv->lock );
}
 
if ( (t->c_lflag & ISIG) && (t->c_cc[ VQUIT ] == c) ) {
discardc = true;
if ( 0 == (t->c_lflag & NOFLSH) )
size = 0;
// raise could be a non-local jump - we should unlock mutex
cyg_drv_mutex_unlock( &priv->lock );
 
// FIXME: what if raise returns != 0?
raise( SIGQUIT );
cyg_drv_mutex_lock( &priv->lock );
}
#endif
if (!discardc) {
buf[size++] = c;
if ( t->c_lflag & ECHO ) {
clen = 1;
// FIXME: what about error or non-blocking?
termios_write( handle, &c, &clen );
}
}
cyg_drv_mutex_unlock( &priv->lock );
} // while
 
*len = size;
return ENOERR;
}
 
 
//==========================================================================
 
static cyg_bool
termios_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
struct termios_private_info *priv = (struct termios_private_info *)t->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
 
// Just pass it on to next driver level
return cyg_io_select( chan, which, info );
}
 
 
//==========================================================================
 
static Cyg_ErrNo
termios_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf,
cyg_uint32 *len)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
struct termios_private_info *priv = (struct termios_private_info *)t->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
Cyg_ErrNo res = ENOERR;
 
switch (key) {
case CYG_IO_GET_CONFIG_TERMIOS:
{
if ( *len < sizeof(struct termios) ) {
return -EINVAL;
}
cyg_drv_mutex_lock( &priv->lock );
*(struct termios *)buf = priv->termios;
cyg_drv_mutex_unlock( &priv->lock );
*len = sizeof(struct termios);
}
break;
default: // Assume this is a 'serial' driver control
res = cyg_io_get_config(chan, key, buf, len);
} // switch
return res;
}
 
 
//==========================================================================
 
 
static Cyg_ErrNo
termios_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len)
{
cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
struct termios_private_info *priv = (struct termios_private_info *)t->priv;
cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
Cyg_ErrNo res = ENOERR;
 
switch (key) {
case CYG_IO_SET_CONFIG_TERMIOS:
{
setattr_struct *attr = (setattr_struct *)buf;
int optact = attr->optact;
 
if ( *len != sizeof( *attr ) ) {
return -EINVAL;
}
 
CYG_ASSERT( (optact == TCSAFLUSH) || (optact == TCSADRAIN) ||
(optact == TCSANOW), "Invalid optact" );
cyg_drv_mutex_lock( &priv->lock );
if ( ( TCSAFLUSH == optact ) ||
( TCSADRAIN == optact ) ) {
res = cyg_io_get_config( chan,
CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN,
NULL, NULL );
CYG_ASSERT( ENOERR == res, "Drain request failed" );
}
if ( TCSAFLUSH == optact ) {
res = cyg_io_get_config( chan,
CYG_IO_GET_CONFIG_SERIAL_INPUT_FLUSH,
NULL, NULL );
CYG_ASSERT( ENOERR == res, "Flush request failed" );
}
res = set_attr( attr->termios_p, priv );
cyg_drv_mutex_unlock( &priv->lock );
return res;
}
default: // Pass on to serial driver
res = cyg_io_set_config(chan, key, buf, len);
}
return res;
}
 
 
//==========================================================================
 
#endif // ifdef CYGPKG_IO_SERIAL_TERMIOS
 
// EOF termiostty.c
/common/haldiag.c
0,0 → 1,138
//==========================================================================
//
// io/serial/common/haldiag.c
//
// Serial I/O interface module using HAL I/O routines
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// 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
// Date: 1999-02-04
// Purpose: HAL/diag serial driver
// Description:
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <pkgconf/io.h>
#include <pkgconf/io_serial.h>
#ifdef CYGPKG_IO_SERIAL_HALDIAG
#include <cyg/io/io.h>
#include <cyg/io/devtab.h>
#include <cyg/io/serial.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/hal_diag.h>
 
static bool haldiag_init(struct cyg_devtab_entry *tab);
static bool haldiag_putc(serial_channel *chan, unsigned char c);
static unsigned char haldiag_getc(serial_channel *chan);
static Cyg_ErrNo haldiag_set_config(serial_channel *chan, cyg_uint32 key,
const void *xbuf, cyg_uint32 *len);
 
static SERIAL_FUNS(haldiag_funs,
haldiag_putc,
haldiag_getc,
haldiag_set_config,
0, // start xmit - not used
0 // stop xmit - not used
);
 
static int _no_data;
static SERIAL_CHANNEL(haldiag_channel0,
haldiag_funs,
_no_data,
CYG_SERIAL_BAUD_DEFAULT,
CYG_SERIAL_STOP_DEFAULT,
CYG_SERIAL_PARITY_DEFAULT,
CYG_SERIAL_WORD_LENGTH_DEFAULT,
CYG_SERIAL_FLAGS_DEFAULT
);
DEVTAB_ENTRY(haldiag_io0,
"/dev/haldiag",
0, // Does not depend on a lower level interface
&cyg_io_serial_devio,
haldiag_init,
0, // No initialization/lookup needed
&haldiag_channel0);
 
static void
haldiag_config_port(serial_channel *chan)
{
}
 
static bool
haldiag_init(struct cyg_devtab_entry *tab)
{
serial_channel *chan = (serial_channel *)tab->priv;
#ifdef CYGDBG_IO_INIT
diag_printf("HAL/diag SERIAL init\n");
#endif
haldiag_config_port(chan);
return true;
}
 
// Return 'true' if character is sent to device
static bool
haldiag_putc(serial_channel *chan, unsigned char c)
{
HAL_DIAG_WRITE_CHAR(c);
return true;
}
 
static unsigned char
haldiag_getc(serial_channel *chan)
{
unsigned char c;
HAL_DIAG_READ_CHAR(c);
return c;
}
 
static Cyg_ErrNo
haldiag_set_config(serial_channel *chan, cyg_uint32 key, const void *xbuf,
cyg_uint32 *len)
{
switch (key) {
case CYG_IO_SET_CONFIG_SERIAL_INFO:
diag_printf("%s\n", __FUNCTION__);
return ENOERR;
default:
return -EINVAL;
}
}
#endif // CYGPKG_IO_SERIAL_HALDIAG

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.