Mercurial > ecos
diff packages/io/serial/current/src/common/serial.c @ 86:6512add4f5d6 ecos-sw-2000-05-05
Merge from eCos master repository on 2000-05-05-07:47:05-BST
| author | jlarmour |
|---|---|
| date | Fri, 05 May 2000 17:31:08 +0000 |
| parents | 6736c52df507 |
| children | afdeb44241de |
line wrap: on
line diff
--- a/packages/io/serial/current/src/common/serial.c +++ b/packages/io/serial/current/src/common/serial.c @@ -31,10 +31,10 @@ //========================================================================== //#####DESCRIPTIONBEGIN#### // -// Author(s): gthomas -// Contributors: gthomas -// Date: 1999-02-04 -// Purpose: Top level serial driver +// Author(s): gthomas +// Contributors: gthomas, grante +// Date: 1999-02-04 +// Purpose: Top level serial driver // Description: // //####DESCRIPTIONEND#### @@ -42,6 +42,8 @@ //========================================================================== #include <pkgconf/io.h> +#include <pkgconf/io_serial.h> + #include <cyg/io/io.h> #include <cyg/io/devtab.h> #include <cyg/io/serial.h> @@ -62,10 +64,28 @@ DEVIO_TABLE(cyg_io_serial_devio, 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 CYGINT_IO_SERIAL_BLOCK_TRANSFER +static bool serial_data_rcv_req(serial_channel *chan, int avail, + int* space_avail, unsigned char** space); +static void serial_data_rcv_done(serial_channel *chan); +static bool serial_data_xmt_req(serial_channel *chan, int space, + int* chars_avail, unsigned char** chars); +static void serial_data_xmt_done(serial_channel *chan); +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); +#else SERIAL_CALLBACKS(cyg_io_serial_callbacks, serial_init, serial_xmt_char, serial_rcv_char); +#endif + static void serial_init(serial_channel *chan) @@ -77,6 +97,9 @@ serial_init(serial_channel *chan) #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); @@ -88,6 +111,9 @@ serial_init(serial_channel *chan) #endif chan->in_cbuf.waiting = false; chan->in_cbuf.abort = false; +#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING + chan->in_cbuf.blocking = true; +#endif cyg_drv_mutex_init(&chan->in_cbuf.lock); cyg_drv_cond_init(&chan->in_cbuf.wait, &chan->in_cbuf.lock); } @@ -108,6 +134,7 @@ serial_write(cyg_io_handle_t handle, con cbuf->abort = false; cyg_drv_mutex_lock(&cbuf->lock); + if (cbuf->len == 0) { // Non interrupt driven (i.e. polled) operation while (size-- > 0) { @@ -123,14 +150,24 @@ serial_write(cyg_io_handle_t handle, con cbuf->waiting = true; // Buffer full - wait for space (funs->start_xmit)(chan); // Make sure xmit is running + // Check flag: 'start_xmit' may have obviated the need to wait :-) if (cbuf->waiting) { - // Note: 'start_xmit' may have obviated the need to wait :-) +#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] cyg_drv_cond_wait(&cbuf->wait); cbuf->pending -= size; } if (cbuf->abort) { // Give up! + *len -= size; // number of characters actually sent cbuf->abort = false; cbuf->waiting = false; res = -EINTR; @@ -168,6 +205,7 @@ serial_read(cyg_io_handle_t handle, void cbuf->abort = false; cyg_drv_mutex_lock(&cbuf->lock); + if (cbuf->len == 0) { // Non interrupt driven (i.e. polled) operation while (size++ < *len) { @@ -181,6 +219,13 @@ serial_read(cyg_io_handle_t handle, void if (++cbuf->get == cbuf->len) cbuf->get = 0; 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; @@ -199,6 +244,7 @@ serial_read(cyg_io_handle_t handle, void #endif // CYGDBG_DIAG_BUF if (cbuf->abort) { // Give up! + *len = size; // characters actually read cbuf->abort = false; cbuf->waiting = false; res = -EINTR; @@ -228,7 +274,8 @@ serial_get_config(cyg_io_handle_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; + cbuf_t *out_cbuf = &chan->out_cbuf; + cbuf_t *in_cbuf = &chan->in_cbuf; serial_funs *funs = chan->funs; switch (key) { @@ -239,64 +286,112 @@ serial_get_config(cyg_io_handle_t handle *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 = chan->in_cbuf.len; + if (p->rx_bufsize) { + p->rx_count = chan->in_cbuf.put - chan->in_cbuf.get; + if (p->rx_count < 0) + p->rx_count += p->rx_bufsize; + } + else + p->rx_count = 0; + + p->tx_bufsize = chan->out_cbuf.len; + if (p->tx_bufsize) { + p->tx_count = chan->out_cbuf.put - chan->out_cbuf.get; + if (p->tx_count < 0) + p->tx_count += p->tx_bufsize; + } + else + p->tx_count = 0; + } + 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 + 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 (cbuf->pending || (cbuf->get != cbuf->put)) { - cbuf->waiting = true; - cyg_drv_cond_wait(&cbuf->wait); + while (out_cbuf->pending || (out_cbuf->get != out_cbuf->put)) { + out_cbuf->waiting = true; + cyg_drv_cond_wait(&out_cbuf->wait); } cyg_drv_dsr_unlock(); - cyg_drv_mutex_unlock(&cbuf->lock); + cyg_drv_mutex_unlock(&out_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 + 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 (cbuf->waiting) { - cbuf->abort = true; - cyg_drv_cond_signal(&cbuf->wait); - cbuf->waiting = false; + if (in_cbuf->waiting) { + in_cbuf->abort = true; + cyg_drv_cond_signal(&in_cbuf->wait); + in_cbuf->waiting = false; } - cbuf->get = cbuf->put; // Flush buffered input + in_cbuf->get = in_cbuf->put; // Flush buffered input cyg_drv_dsr_unlock(); - cyg_drv_mutex_unlock(&cbuf->lock); + 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 - cbuf = &chan->in_cbuf; - if (cbuf->len != 0) { - cbuf->abort = true; - cyg_drv_cond_signal(&cbuf->wait); + if (in_cbuf->len != 0) { + in_cbuf->abort = true; + cyg_drv_cond_signal(&in_cbuf->wait); } - cbuf = &chan->out_cbuf; - if (cbuf->len != 0) { - cbuf->abort = true; - cyg_drv_cond_signal(&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 (cbuf->len == 0) break; // Nothing to do if not buffered - cyg_drv_mutex_lock(&cbuf->lock); // Stop any further output processing + 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 (cbuf->get != cbuf->put) { - cbuf->get = cbuf->put; // Empties queue! + if (out_cbuf->get != out_cbuf->put) { + out_cbuf->get = out_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; + 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(&cbuf->lock); + cyg_drv_mutex_unlock(&out_cbuf->lock); + break; + +#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING + case CYG_IO_GET_CONFIG_SERIAL_READ_BLOCKING: + if (*len < sizeof(cyg_uint32)) { + return -EINVAL; + } + *(cyg_uint32*)xbuf = (in_cbuf->blocking) ? 1 : 0; break; + + case CYG_IO_GET_CONFIG_SERIAL_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; } @@ -309,6 +404,10 @@ serial_set_config(cyg_io_handle_t handle 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; cyg_serial_info_t *buf = (cyg_serial_info_t *)xbuf; @@ -322,6 +421,20 @@ serial_set_config(cyg_io_handle_t handle res = -EINVAL; } break; +#ifdef CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING + case CYG_IO_SET_CONFIG_SERIAL_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_SERIAL_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 default: res = -EINVAL; } @@ -366,6 +479,15 @@ serial_rcv_char(serial_channel *chan, un { cbuf_t *cbuf = &chan->in_cbuf; +#if 0 + // FIXME: add error handling + int space; + // Check for overflow + space = (cbuf->len + cbuf->put - cbuf->get) % cbuf->len; + if (space == (cbuf->len - 1)) + ; // full since put==get means empty +#endif + cbuf->data[cbuf->put++] = c; if (cbuf->put == cbuf->len) cbuf->put = 0; if (cbuf->waiting) { @@ -385,3 +507,109 @@ serial_rcv_char(serial_channel *chan, un } } +//---------------------------------------------------------------------------- +// Block transfer functions. Not all drivers require these. +#ifdef CYGINT_IO_SERIAL_BLOCK_TRANSFER + +static bool +serial_data_rcv_req(serial_channel *chan, int avail, + int* space_avail, unsigned char** space) +{ + cbuf_t *cbuf = &chan->in_cbuf; + int gap; + + // Check for space + gap = (cbuf->len + cbuf->put - cbuf->get) % cbuf->len; + if (gap == (cbuf->len - 1)) + return false; // full since put==get means empty + + 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 - 1; + } 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 - 1; + if (gap < 0) { + // If failed, the gap is between put and the end of buffer + gap = cbuf->len - cbuf->put - 1; + } + } + + if (avail < gap) gap = avail; // bound by what's available from hw + + *space_avail = gap; + *space = &cbuf->data[cbuf->put]; + cbuf->put += gap; + + if (cbuf->put == cbuf->len) cbuf->put = 0; + + return true; +} + +static void +serial_data_rcv_done(serial_channel *chan) +{ + cbuf_t *cbuf = &chan->in_cbuf; + if (cbuf->waiting) { + cbuf->waiting = false; + cyg_drv_cond_signal(&cbuf->wait); + } +} + +static bool +serial_data_xmt_req(serial_channel *chan, int space, + int* chars_avail, unsigned char** chars) +{ + cbuf_t *cbuf = &chan->out_cbuf; + int avail; + + // Available data (G = get, P = put, x = data, . = empty) + // 0: no data + // negative: xxxxP.....Gxxx [offer last chunk only] + // positive: ..GxxxxxP..... + avail = cbuf->put - cbuf->get; + if (0 == avail) + return false; + if (avail < 0) { + avail = cbuf->len - cbuf->get; + } + + if (avail > space) avail = space; // bound by space in hardware + + *chars_avail = avail; + *chars = &cbuf->data[cbuf->get]; + cbuf->get += avail; + + if (cbuf->get == cbuf->len) cbuf->get = 0; + + return true; +} + +static void +serial_data_xmt_done(serial_channel *chan) +{ + cbuf_t *cbuf = &chan->out_cbuf; + serial_funs *funs = chan->funs; + int space; + + (funs->stop_xmit)(chan); // Done with transmit + + 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); + } + } +} + +#endif // CYGINT_IO_SERIAL_BLOCK_TRANSFER
