diff packages/io/serial/current/src/common/serial.c @ 2:443894e2e912 ecos-v1_2_1-release

Block commit of eCos version 1.2.1
author jlarmour
date Tue, 11 May 1999 12:24:34 +0000
parents
children 1d7f19c9e4d1
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/io/serial/current/src/common/serial.c
@@ -0,0 +1,340 @@
+//==========================================================================
+//
+//      io/serial/common/serial.c
+//
+//      High level serial driver
+//
+//==========================================================================
+//####COPYRIGHTBEGIN####
+//
+// -------------------------------------------
+// The contents of this file are subject to the Cygnus eCos Public License
+// Version 1.0 (the "License"); you may not use this file except in
+// compliance with the License.  You may obtain a copy of the License at
+// http://sourceware.cygnus.com/ecos
+// 
+// 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 Cygnus Operating System, released
+// September 30, 1998.
+// 
+// The Initial Developer of the Original Code is Cygnus.  Portions created
+// by Cygnus are Copyright (C) 1998,1999 Cygnus Solutions.  All Rights Reserved.
+// -------------------------------------------
+//
+//####COPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):   gthomas
+// Contributors:  gthomas
+// Date:        1999-02-04
+// Purpose:     Top level serial driver
+// Description: 
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/io.h>
+#include <cyg/io/io.h>
+#include <cyg/io/devtab.h>
+#include <cyg/io/serial.h>
+#include <cyg/infra/diag.h>
+
+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_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(serial_devio,
+            serial_write,
+            serial_read,
+            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);
+SERIAL_CALLBACKS(serial_callbacks, 
+                 serial_init, 
+                 serial_xmt_char, 
+                 serial_rcv_char);
+
+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;
+        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;
+    }
+    if (chan->in_cbuf.len != 0) {
+#ifdef CYGDBG_IO_INIT
+        diag_printf("Set input buffer - buf: %x len: %d\n", chan->in_cbuf.data, chan->in_cbuf.len);
+#endif
+        chan->in_cbuf.waiting = false;
+        chan->in_cbuf.abort = false;
+        cyg_drv_mutex_init(&chan->in_cbuf.lock);
+        cyg_drv_cond_init(&chan->in_cbuf.wait, &chan->in_cbuf.lock);
+    }
+    chan->init = true;
+}
+
+static Cyg_ErrNo 
+serial_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len)
+{
+    cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
+    serial_channel *chan = (serial_channel *)t->priv;
+    serial_funs *funs = chan->funs;
+    cyg_int32 size = *len;
+    cyg_uint8 *buf = (cyg_uint8 *)_buf;
+    int next;
+    cbuf_t *cbuf = &chan->out_cbuf;
+    Cyg_ErrNo res = ENOERR;
+
+    cyg_drv_mutex_lock(&cbuf->lock);
+    if (cbuf->len == 0) {
+        // Non interrupt driven (i.e. polled) operation
+        while (size-- > 0) {
+            while ((funs->putc)(chan, *buf) == false) ;  // Ignore full, keep trying
+            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 (next == cbuf->get) {                
+                cbuf->waiting = true;
+                // Buffer full - wait for space
+                (funs->start_xmit)(chan);  // Make sure xmit is running
+                cbuf->pending += size;  // Have this much more to send [eventually]
+                cyg_drv_cond_wait(&cbuf->wait);
+                cbuf->pending -= size;
+                if (cbuf->abort) {
+                    // Give up!
+                    cbuf->abort = false;
+                    cbuf->waiting = false;
+                    res = -EINTR;
+                    break;
+                }
+            } else {
+                cbuf->data[cbuf->put++] = *buf++;
+                cbuf->put = next;
+                size--;  // Only count if actually sent!
+            }
+        }
+        cyg_drv_dsr_unlock();
+        (funs->start_xmit)(chan);  // Start output as necessary
+    }
+    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;
+
+    cyg_drv_mutex_lock(&cbuf->lock);
+    if (cbuf->len == 0) {
+        // Non interrupt driven (i.e. polled) operation
+        while (size++ < *len) {
+            *buf++ = (funs->getc)(chan);
+        }
+    } else {
+        cyg_drv_dsr_lock();  // Avoid races
+        while (size < *len) {
+            if (cbuf->get != cbuf->put) {
+                *buf++ = cbuf->data[cbuf->get];
+                if (++cbuf->get == cbuf->len) cbuf->get = 0;
+                size++;
+            } else {
+                cbuf->waiting = true;
+                cyg_drv_cond_wait(&cbuf->wait);
+                if (cbuf->abort) {
+                    // Give up!
+                    cbuf->abort = false;
+                    cbuf->waiting = false;
+                    res = -EINTR;
+                    break;
+                }
+            }
+        }
+        cyg_drv_dsr_unlock();
+    }
+    cyg_drv_mutex_unlock(&cbuf->lock);
+    return res;
+}
+
+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 *cbuf = &chan->out_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_OUTPUT_DRAIN:
+// Wait for any pending output to complete
+        if (cbuf->len == 0) break;  // Nothing to do if not buffered
+        cyg_drv_mutex_lock(&cbuf->lock);  // Stop any further output processing
+        cyg_drv_dsr_lock();
+        while (cbuf->pending || (cbuf->get != cbuf->put)) {
+            cbuf->waiting = true;
+            cyg_drv_cond_wait(&cbuf->wait);
+        }
+        cyg_drv_dsr_unlock();
+        cyg_drv_mutex_unlock(&cbuf->lock);
+        break;
+    case CYG_IO_GET_CONFIG_SERIAL_INPUT_FLUSH:
+        // Flush any buffered input
+        cbuf = &chan->in_cbuf;
+        if (cbuf->len == 0) break;  // Nothing to do if not buffered
+        cyg_drv_mutex_lock(&cbuf->lock);  // Stop any further input processing
+        cyg_drv_dsr_lock();
+        if (cbuf->waiting) {
+            cbuf->abort = true;
+            cyg_drv_cond_signal(&cbuf->wait);
+            cbuf->waiting = false;
+        }
+        cbuf->get = cbuf->put;  // Flush buffered input
+        cyg_drv_dsr_unlock();
+        cyg_drv_mutex_unlock(&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
+        cbuf = &chan->in_cbuf;
+        if (cbuf->len != 0) {
+            cbuf->abort = true;
+            cyg_drv_cond_signal(&cbuf->wait);
+        }
+        cbuf = &chan->out_cbuf;
+        if (cbuf->len != 0) {
+            cbuf->abort = true;
+            cyg_drv_cond_signal(&cbuf->wait);
+        }
+        break;
+    case CYG_IO_GET_CONFIG_SERIAL_OUTPUT_FLUSH:
+// Throw away any pending output
+        if (cbuf->len == 0) break;  // Nothing to do if not buffered
+        cyg_drv_mutex_lock(&cbuf->lock);  // Stop any further output processing
+        cyg_drv_dsr_lock();
+        if (cbuf->get != cbuf->put) {
+            cbuf->get = cbuf->put;  // Empties queue!
+            (funs->stop_xmit)(chan);  // Done with transmit
+        }
+        if (cbuf->waiting) {
+            cbuf->abort = true;
+            cyg_drv_cond_signal(&cbuf->wait);
+            cbuf->waiting = false;
+        }
+        cyg_drv_dsr_unlock();
+        cyg_drv_mutex_unlock(&cbuf->lock);
+        break;
+    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;
+    serial_funs *funs = chan->funs;
+    cyg_serial_info_t *buf = (cyg_serial_info_t *)xbuf;
+
+    switch (key) {
+    case CYG_IO_SET_CONFIG_SERIAL_INFO:
+        if (*len != sizeof(cyg_serial_info_t)) {
+            return -EINVAL;
+        }
+        if ((funs->set_config)(chan, buf) != true) {
+            // Device is not happy with paramters or they cannot be set
+            res = -EINVAL;
+        }
+        break;
+    default:
+        res = -EINVAL;
+    }
+    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;
+
+    while (cbuf->get != cbuf->put) {
+        c = cbuf->data[cbuf->get];
+        if ((funs->putc)(chan, c)) {
+            cbuf->get++;
+            if (cbuf->get == cbuf->len) cbuf->get = 0;
+            if (cbuf->waiting) {
+                // See if there is now enough room to restart writer
+                space = (cbuf->len + cbuf->get) - cbuf->put;
+                if (space > cbuf->len) space -= cbuf->len;
+                if (space >= cbuf->low_water) {
+                    cbuf->waiting = false;
+                    cyg_drv_cond_broadcast(&cbuf->wait);
+                }
+            }
+        } else {
+            return;  // Need to wait for more space
+        }
+    }
+    (funs->stop_xmit)(chan);  // Done with transmit
+    if (cbuf->waiting) {
+        cbuf->waiting = false;
+        cyg_drv_cond_signal(&cbuf->wait);
+    }
+}
+
+static void
+serial_rcv_char(serial_channel *chan, unsigned char c)
+{
+    cbuf_t *cbuf = &chan->in_cbuf;
+
+    cbuf->data[cbuf->put++] = c;
+    if (cbuf->put == cbuf->len) cbuf->put = 0;
+    if (cbuf->waiting) {
+        cbuf->waiting = false;
+        cyg_drv_cond_signal(&cbuf->wait);
+    }
+}
+