Mercurial > ecos-v2_0-branch
diff packages/devs/serial/loop/current/src/loop_serial.c @ 115:6ed91473a1cd ecos-sw-2000-08-21
Merge from eCos master repository on 2000-08-21-22:40:54-BST
| author | jlarmour |
|---|---|
| date | Fri, 25 Aug 2000 17:32:38 +0000 |
| parents | |
| children | e0c0827131d1 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/devs/serial/loop/current/src/loop_serial.c @@ -0,0 +1,448 @@ +//========================================================================== +// +// loop_serial.c +// +// Loopback serial device driver +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 1999-02-25 +// Purpose: Loopback serial device driver +// Description: This device driver implements a pair of serial lines that are +// connected back-to-back. Data output to one will appear as +// input on the other. This process in in part driven by an alarm +// object which provides a degree of separation between the two +// channels. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/hal.h> +#include <pkgconf/io_serial.h> +#include <pkgconf/io_serial_loop.h> +#include <cyg/hal/hal_io.h> + +#include <cyg/io/io.h> +#include <cyg/io/devtab.h> +#include <cyg/io/serial.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/kernel/kapi.h> + +#ifdef CYGPKG_IO_SERIAL_LOOP + +//------------------------------------------------------------------------- + +extern void diag_printf(const char *fmt, ...); + +//------------------------------------------------------------------------- +// Forward definitions + +static bool loop_serial_init(struct cyg_devtab_entry *tab); +static bool loop_serial_putc(serial_channel *chan, unsigned char c); +static Cyg_ErrNo loop_serial_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); +static unsigned char loop_serial_getc(serial_channel *chan); +static Cyg_ErrNo loop_serial_set_config(serial_channel *chan, cyg_uint32 key, + const void *xbuf, cyg_uint32 *len); +static void loop_serial_start_xmit(serial_channel *chan); +static void loop_serial_stop_xmit(serial_channel *chan); + +#ifndef CYGPKG_IO_SERIAL_LOOP_POLLED_MODE +static void alarm_handler(cyg_handle_t alarm, cyg_addrword_t data); +#endif + +//------------------------------------------------------------------------- +// Alarm object for feeding data back into serial channels + +static cyg_alarm alarm_obj; + +static cyg_handle_t alarm_handle; + +//------------------------------------------------------------------------- +// Transfer FIFOs + +#define FIFO_SIZE 16 + +struct fifo +{ + cyg_bool tx_enable; + volatile int head; + volatile int tail; + volatile int num; + volatile char buf[FIFO_SIZE+1]; +}; + +static struct fifo fifo0 = { false, 0, 0, 0 }; // from serial0 to serial1 +static struct fifo fifo1 = { false, 0, 0, 0 }; // from serial1 to serial0 + +//------------------------------------------------------------------------- + +#define BUFSIZE 128 + +//------------------------------------------------------------------------- +// Info for each serial device controlled + +typedef struct loop_serial_info { + struct fifo *write_fifo; + struct fifo *read_fifo; +} loop_serial_info; + +//------------------------------------------------------------------------- +// Callback functions exported by this driver + +static SERIAL_FUNS(loop_serial_funs, + loop_serial_putc, + loop_serial_getc, + loop_serial_set_config, + loop_serial_start_xmit, + loop_serial_stop_xmit + ); + +//------------------------------------------------------------------------- +// Hardware info for each serial line + +#ifdef CYGPKG_IO_SERIAL_LOOP_SERIAL0 +static loop_serial_info loop_serial_info0 = { + &fifo0, + &fifo1 +}; +#if CYGNUM_IO_SERIAL_LOOP_SERIAL0_BUFSIZE > 0 +static unsigned char loop_serial_out_buf0[CYGNUM_IO_SERIAL_LOOP_SERIAL0_BUFSIZE]; +static unsigned char loop_serial_in_buf0[CYGNUM_IO_SERIAL_LOOP_SERIAL0_BUFSIZE]; +#endif +#endif // CYGPKG_IO_SERIAL_LOOP_SERIAL0 + +#ifdef CYGPKG_IO_SERIAL_LOOP_SERIAL1 +static loop_serial_info loop_serial_info1 = { + &fifo1, + &fifo0 +}; +#if CYGNUM_IO_SERIAL_LOOP_SERIAL1_BUFSIZE > 0 +static unsigned char loop_serial_out_buf1[CYGNUM_IO_SERIAL_LOOP_SERIAL1_BUFSIZE]; +static unsigned char loop_serial_in_buf1[CYGNUM_IO_SERIAL_LOOP_SERIAL1_BUFSIZE]; +#endif +#endif // CYGPKG_IO_SERIAL_LOOP_SERIAL1 + +//------------------------------------------------------------------------- +// Channel descriptions: + +#ifdef CYGPKG_IO_SERIAL_LOOP_POLLED_MODE +#define SIZEOF_BUF(_x_) 0 +#else +#define SIZEOF_BUF(_x_) sizeof(_x_) +#endif + +#ifdef CYGPKG_IO_SERIAL_LOOP_SERIAL0 +#if CYGNUM_IO_SERIAL_LOOP_SERIAL0_BUFSIZE > 0 +static SERIAL_CHANNEL_USING_INTERRUPTS(loop_serial_channel0, + loop_serial_funs, + loop_serial_info0, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_LOOP_SERIAL0_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &loop_serial_out_buf0[0], + SIZEOF_BUF(loop_serial_out_buf0), + &loop_serial_in_buf0[0], + SIZEOF_BUF(loop_serial_in_buf0) + ); +#else +static SERIAL_CHANNEL(loop_serial_channel0, + loop_serial_funs, + loop_serial_info0, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_LOOP_SERIAL0_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT + ); +#endif +#endif // CYGPKG_IO_SERIAL_LOOP_SERIAL0 + +#ifdef CYGPKG_IO_SERIAL_LOOP_SERIAL1 +#if CYGNUM_IO_SERIAL_LOOP_SERIAL1_BUFSIZE > 0 +static SERIAL_CHANNEL_USING_INTERRUPTS(loop_serial_channel1, + loop_serial_funs, + loop_serial_info1, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_LOOP_SERIAL1_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT, + &loop_serial_out_buf1[0], + SIZEOF_BUF(loop_serial_out_buf1), + &loop_serial_in_buf1[0], + SIZEOF_BUF(loop_serial_in_buf1) + ); +#else +static SERIAL_CHANNEL(loop_serial_channel1, + loop_serial_funs, + loop_serial_info1, + CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_LOOP_SERIAL1_BAUD), + CYG_SERIAL_STOP_DEFAULT, + CYG_SERIAL_PARITY_DEFAULT, + CYG_SERIAL_WORD_LENGTH_DEFAULT, + CYG_SERIAL_FLAGS_DEFAULT + ); +#endif +#endif // CYGPKG_IO_SERIAL_LOOP_SERIAL1 + +//------------------------------------------------------------------------- +// And finally, the device table entries: + +#ifdef CYGPKG_IO_SERIAL_LOOP_SERIAL0 +DEVTAB_ENTRY(loop_serial_io0, + CYGDAT_IO_SERIAL_LOOP_SERIAL0_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_serial_devio, + loop_serial_init, + loop_serial_lookup, // Serial driver may need initializing + &loop_serial_channel0 + ); +#endif // CYGPKG_IO_SERIAL_LOOP_SERIAL0 + +#ifdef CYGPKG_IO_SERIAL_LOOP_SERIAL1 +DEVTAB_ENTRY(loop_serial_io1, + CYGDAT_IO_SERIAL_LOOP_SERIAL1_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_serial_devio, + loop_serial_init, + loop_serial_lookup, // Serial driver may need initializing + &loop_serial_channel1 + ); +#endif // CYGPKG_IO_SERIAL_LOOP_SERIAL1 + +//------------------------------------------------------------------------- + +static bool +loop_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init) +{ +// loop_serial_info *loop_chan = (loop_serial_info *)chan->dev_priv; + + if (new_config != &chan->config) { + chan->config = *new_config; + } + return true; +} + +//------------------------------------------------------------------------- +// Function to initialize the device. Called at bootstrap time. + +bool loop_serial_init(struct cyg_devtab_entry *tab) +{ + serial_channel *chan = (serial_channel *)tab->priv; +// loop_serial_info *loop_chan = (loop_serial_info *)chan->dev_priv; + + (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices + +#ifndef CYGPKG_IO_SERIAL_LOOP_POLLED_MODE + + // Set up alarm for feeding data back into channels + + cyg_alarm_create( cyg_real_time_clock(), + alarm_handler, + 0, + &alarm_handle, + &alarm_obj); + + cyg_alarm_initialize( alarm_handle, 1, 1 ); + +#endif + + loop_serial_config_port(chan, &chan->config, true); + + return true; +} + +//------------------------------------------------------------------------- +// This routine is called when the device is "looked" up (i.e. attached) + +static Cyg_ErrNo +loop_serial_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + serial_channel *chan = (serial_channel *)(*tab)->priv; + (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices + return ENOERR; +} + +//------------------------------------------------------------------------- +// Return 'true' if character is sent to device + +bool +loop_serial_putc(serial_channel *chan, unsigned char c) +{ + loop_serial_info *loop_chan = (loop_serial_info *)chan->dev_priv; + + struct fifo *f = loop_chan->write_fifo; + + if( f->num == FIFO_SIZE ) + return false; + + f->buf[f->tail] = c; + f->num++; + f->tail++; + if( f->tail == sizeof(f->buf) ) + f->tail = 0; + + return true; +} + +//------------------------------------------------------------------------- + +unsigned char +loop_serial_getc(serial_channel *chan) +{ + unsigned char c; + loop_serial_info *loop_chan = (loop_serial_info *)chan->dev_priv; + + struct fifo *f = loop_chan->read_fifo; + + while( f->num == 0 ) + continue; + + c = f->buf[f->head]; + f->num--; + f->head++; + if( f->head == sizeof(f->buf) ) + f->head = 0; + + return c; +} + +//------------------------------------------------------------------------- + +static Cyg_ErrNo +loop_serial_set_config(serial_channel *chan, cyg_uint32 key, + const void *xbuf, cyg_uint32 *len) +{ + switch (key) { + case CYG_IO_SET_CONFIG_SERIAL_INFO: + { + cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf; + if ( *len < sizeof(cyg_serial_info_t) ) { + return -EINVAL; + } + *len = sizeof(cyg_serial_info_t); + if ( true != loop_serial_config_port(chan, config, false) ) + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return ENOERR; +} + +//------------------------------------------------------------------------- +// Enable the transmitter on the device + +static void +loop_serial_start_xmit(serial_channel *chan) +{ +#ifndef CYGPKG_IO_SERIAL_LOOP_POLLED_MODE + loop_serial_info *loop_chan = (loop_serial_info *)chan->dev_priv; + + loop_chan->write_fifo->tx_enable = true; + + (chan->callbacks->xmt_char)(chan); +#endif +} + +//------------------------------------------------------------------------- +// Disable the transmitter on the device + +static void +loop_serial_stop_xmit(serial_channel *chan) +{ +#ifndef CYGPKG_IO_SERIAL_LOOP_POLLED_MODE + loop_serial_info *loop_chan = (loop_serial_info *)chan->dev_priv; + + loop_chan->write_fifo->tx_enable = false; + +#endif +} + +//------------------------------------------------------------------------- + +static void alarm_handler(cyg_handle_t alarm, cyg_addrword_t data) +{ + serial_channel *chan0 = &loop_serial_channel0; + serial_channel *chan1 = &loop_serial_channel1; + + while( fifo0.num ) + { + // Data ready for delivery to serial1 + + struct fifo *f = &fifo0; + char c; + + c = f->buf[f->head]; + f->num--; + f->head++; + if( f->head == sizeof(f->buf) ) + f->head = 0; + + (chan1->callbacks->rcv_char)(chan1, c); + if( f->tx_enable ) + (chan0->callbacks->xmt_char)(chan0); + } + + while( fifo1.num ) + { + // Data ready for delivery to serial0 + + struct fifo *f = &fifo1; + char c; + + c = f->buf[f->head]; + f->num--; + f->head++; + if( f->head == sizeof(f->buf) ) + f->head = 0; + + (chan0->callbacks->rcv_char)(chan0, c); + if( f->tx_enable ) + (chan1->callbacks->xmt_char)(chan1); + } + + + +} + + +#endif // CYGPKG_IO_SERIAL_LOOP + +//------------------------------------------------------------------------- +// EOF loop.c
