# HG changeset patch # User jlarmour # Date 1063380769 0 # Node ID 349e2eadf98df1dd2267cb66e101f1088116d0d3 # Parent 2ad59a585529a934b51225ad77f96eb20bebde5b * src/common/serial.c: Fixed bug for XON/XOFF flow control that would cause multiple XON (or XOFF) characters to be sent for a single flow control state transition. Fixed bug in serial_write() that would cause output data to be discarded if using polled mode with flow control, and output was blocked due to flow control. Fixed bug in serial_data_xmt_req() that would not stop sending data when output was flow controlled off (hardware flow control). diff --git a/packages/io/serial/current/ChangeLog b/packages/io/serial/current/ChangeLog --- a/packages/io/serial/current/ChangeLog +++ b/packages/io/serial/current/ChangeLog @@ -1,3 +1,14 @@ +2003-08-18 Jay Foster + + * src/common/serial.c: Fixed bug for XON/XOFF flow control that + would cause multiple XON (or XOFF) characters to be sent for a + single flow control state transition. + Fixed bug in serial_write() that would cause output data to be + discarded if using polled mode with flow control, and output + was blocked due to flow control. + Fixed bug in serial_data_xmt_req() that would not stop sending + data when output was flow controlled off (hardware flow control). + 2003-09-02 Eric Doenges * src/common/tty.c: Make tty_select prototype match implementation. diff --git a/packages/io/serial/current/src/common/serial.c b/packages/io/serial/current/src/common/serial.c --- a/packages/io/serial/current/src/common/serial.c +++ b/packages/io/serial/current/src/common/serial.c @@ -164,11 +164,17 @@ static __inline__ void throttle_rx( serial_channel *chan, cyg_bool force ) { serial_funs *funs = chan->funs; +#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE + cyg_uint32 prev_flags = chan->flow_desc.flags; +#endif 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 ) { + // send an xoff if not already done (throttled) + if ( force || + ((chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_RX) && + (0==(prev_flags & CYG_SERIAL_FLOW_IN_THROTTLED))) ) { + CYG_ASSERT(force||(chan->flow_desc.xchar=='\0')||(chan->flow_desc.xchar==CYGDAT_IO_SERIAL_FLOW_CONTROL_XOFF_CHAR), "xchar already set (XOFF)"); 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); @@ -193,11 +199,17 @@ static __inline__ void restart_rx( serial_channel *chan, cyg_bool force ) { serial_funs *funs = chan->funs; +#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE + cyg_uint32 prev_flags = chan->flow_desc.flags; +#endif 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 ) { + // send an xon, if we haven't already + if ( force || + ((chan->config.flags & CYGNUM_SERIAL_FLOW_XONXOFF_RX) && + (prev_flags & CYG_SERIAL_FLOW_IN_THROTTLED)) ) { + CYG_ASSERT(force||(chan->flow_desc.xchar=='\0')||(chan->flow_desc.xchar==CYGDAT_IO_SERIAL_FLOW_CONTROL_XON_CHAR), "xchar already set (XON)"); 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 } @@ -265,7 +277,7 @@ serial_init(serial_channel *chan) // 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 + // as it is near-instantaneous if ( (cbuf->len - cbuf->high_water) < 35 ) cbuf->high_water = cbuf->len - 35; // and just in case... @@ -312,7 +324,7 @@ serial_write(cyg_io_handle_t handle, con // 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) ) && + while ( ( 0 != (chan->flow_desc.flags & CYG_SERIAL_FLOW_OUT_THROTTLED) ) || ((funs->putc)(chan, *buf) == false) ) ; // Ignore full, keep trying #else @@ -1113,6 +1125,14 @@ serial_data_xmt_req(serial_channel *chan CYG_ASSERT(false == cbuf->block_mode_xfer_running, "Attempting new block transfer while another is running"); +#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 ) { + (chan->funs->stop_xmit)(chan); // Stop transmitting for now + return CYG_XMT_EMPTY; + } +#endif + // Available data (G = get, P = put, x = data, . = empty) // 0: no data // negative: xxxxP.....Gxxx [offer last chunk only]