Mercurial > flash_v2
diff packages/io/serial/current/src/common/serial.c @ 128:0c2b7be0d798 ecos-sw-2000-10-12
Merge from eCos master repository on 2000-10-12-08:46:24-BST
| author | jlarmour |
|---|---|
| date | Thu, 12 Oct 2000 20:31:43 +0000 |
| parents | 6ed91473a1cd |
| children | eb9fd8c04db3 |
line wrap: on
line diff
--- a/packages/io/serial/current/src/common/serial.c +++ b/packages/io/serial/current/src/common/serial.c @@ -71,12 +71,14 @@ static void serial_indicate_status(seria cyg_serial_line_status_t *s); #endif #if 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); +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_FLOW_CONTROL_HW SERIAL_CALLBACKS(cyg_io_serial_callbacks, serial_init, @@ -269,6 +271,12 @@ serial_init(serial_channel *chan) chan->status_callback = NULL; #endif +#ifdef CYGDBG_USE_ASSERTS +#ifdef 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; } @@ -299,7 +307,8 @@ serial_write(cyg_io_handle_t handle, con ((funs->putc)(chan, *buf) == false) ) ; // Ignore full, keep trying #else - while ((funs->putc)(chan, *buf) == false) ; // Ignore full, keep trying + while ((funs->putc)(chan, *buf) == false) + ; // Ignore full, keep trying #endif buf++; } @@ -316,7 +325,8 @@ serial_write(cyg_io_handle_t handle, con #endif (funs->start_xmit)(chan); // Make sure xmit is running - // Check flag: 'start_xmit' may have obviated the need to wait :-) + // 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. @@ -527,7 +537,8 @@ serial_select(cyg_io_handle_t handle, cy // --------------------------------------------------------------------------- static Cyg_ErrNo -serial_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *xbuf, cyg_uint32 *len) +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; @@ -656,7 +667,8 @@ serial_get_config(cyg_io_handle_t handle // --------------------------------------------------------------------------- static Cyg_ErrNo -serial_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *xbuf, cyg_uint32 *len) +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; @@ -795,8 +807,13 @@ serial_xmt_char(serial_channel *chan) 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 + // 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? @@ -859,6 +876,10 @@ serial_rcv_char(serial_channel *chan, un { 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) @@ -888,12 +909,20 @@ serial_rcv_char(serial_channel *chan, un // If the flow control is not enabled/sufficient and the buffer is // already full, just throw new characters away. - if( cbuf->nb < cbuf->len ) - { + 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; @@ -932,20 +961,47 @@ serial_indicate_status(serial_channel *c #endif // ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS //---------------------------------------------------------------------------- -// Block transfer functions. Not all drivers require these. +// 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 bool +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 false; // full + 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 @@ -970,42 +1026,80 @@ serial_data_rcv_req(serial_channel *chan *space_avail = gap; *space = &cbuf->data[cbuf->put]; - cbuf->put += gap; - cbuf->nb += gap; + + 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; - return true; -} + 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"); -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); } +#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. - cyg_selwakeup( &cbuf->selinfo ); + if (chars_rcvd == cbuf->nb) + cyg_selwakeup( &cbuf->selinfo ); +#endif + +#ifdef CYGDBG_USE_ASSERTS + cbuf->block_mode_xfer_running = false; #endif } -static bool +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 false; + 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 { @@ -1016,22 +1110,36 @@ serial_data_xmt_req(serial_channel *chan *chars_avail = avail; *chars = &cbuf->data[cbuf->get]; - cbuf->get += avail; - cbuf->nb -= avail; - if (cbuf->get == cbuf->len) cbuf->get = 0; + 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 true; + return CYG_XMT_OK; } static void -serial_data_xmt_done(serial_channel *chan) +serial_data_xmt_done(serial_channel *chan, int chars_sent) { cbuf_t *cbuf = &chan->out_cbuf; serial_funs *funs = chan->funs; int space; - (funs->stop_xmit)(chan); // Done with transmit + 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; @@ -1044,6 +1152,10 @@ serial_data_xmt_done(serial_channel *cha cyg_selwakeup( &cbuf->selinfo ); #endif } + +#ifdef CYGDBG_USE_ASSERTS + cbuf->block_mode_xfer_running = false; +#endif } #endif // CYGINT_IO_SERIAL_BLOCK_TRANSFER
