In general, the speed at which data is transmitted is deliberately set to be faster than the speed with which it can be processed by the receiver. This results in a highly efficient utilisation of the available bandwidth — data transfers complete faster — and when data is being transmitted over phone lines, this translates into a direct saving of connect time and therefore of money.
Consequently, input buffers linked to reliable flow control mechanisms are essential for effective communications, as the receiver needs a mechanism for throttling back the transmitter and telling it to stop sending data. The TCommConfig
package enables flow control to be configured at the driver level, relieving application software of the burden of handling this chore. Since multiple handshaking mechanisms may be active simultaneously, the various possibilities are implemented via a bitmapped integer (iHandshake
) and various sets of individually enumerated bitmasks.
The first four of the handshaking bitmasks control what type of flow control is obeyed when we are sending data. We can set up any combination of hardware flow control (where we stop transmitting when any specified permutation of the modem input line CTS, DSR or DCD are low and we then wait for the relevant lines to go high before resuming) and software flow control (where we suspend transmission when we receive a configurable XOFF character and don’t resume until we receive a corresponding XON character).
The most common form of flow control for transmitted data is CTS handshaking (we don’t send any data when the CTS input is low).
The next three of the handshaking bitmasks controls the type of flow control we implement when receiving data, in the expectation that the transmitter will stop sending when told to. Again, we can set up any combination of hardware flow control (where we drop the signal levels on one or both of the modem outputs DTR and RTS to stop the incoming data and then raise the signals again to restart) or software flow control (where we transmit our XOFF character to stop the incoming data and then transmit XON when we are ready to resume). Software flow control is enforced by setting the KConfigSendXoff
bit of iHandshake
, while hardware flow control is enabled by setting either one or both of the KConfigFreeRTS
and KConfigFreeDTR
bits, which indicate whether or not the RTS and DTR outputs are under driver control or follow the user-defined settings — see SetSignals()
for more information.
The most common form of flow control when receiving data is RTS handshaking (we lower our RTS output when we aren’t able to receive anything and then raise it when we are once again in a position to handle incoming data).
Since the rate at which data is being placed in the input buffer can exceed the rate at which it is being taken out, it is probable that the buffer will gradually fill up. When a particular point (the high water mark), is reached and a suitable handshaking mode is enabled, a flow control event is triggered. This could be the transmission of an XOFF character or the lowering of either the RTS or DTR output signals, or any combination of these events. Whatever action is taken will be taken as a signal by the transmitter, which will stop sending. The inward flow of data will then stop, and the buffer will begin to empty. When a particular point (the low water mark) is reached, a matching flow control event is triggered, which could be the transmission of an XON character or the raising of the RTS or DTR signals, or any combination of these events.
If hardware flow control is the only kind enabled for reception, the high water mark is normally set at the 75% full level, at which point one or both of the output signal will be lowered.
If XON/XOFF software flow control is enabled for reception (even if hardware handshaking is also enabled), the high water mark is set to be 5 bytes below the 50% full level, at which point an XOFF character is transmitted for every other character received. This more stringent level is deemed necessary because it normally takes longer for a transmitter to respond when software handshaking is being used
It is possible to force the serial drivers to use this more stringent high water mark when using hardware flow control.
In all cases, the low water mark is set at the 25% full level. Depending on the type of flow control enabled, any combination of sending an XON character or raising one or both of the output signals will occur at this point.
It takes a finite amount of time to respond to flow control and other handshaking events. The term latency is generally used in this context to refer to the time taken between the occurrence of a particular event and the response to it. The maximum handshaking latency in ultimately depends on the frequency of the system clock.
However, the high and low watermarks for inward flow control need to allow for the response times of the systems with which our serial port is communicating rather than the latency associated with our own responses. Suppose it takes one second for a system to which we are connected to respond to a request to stop sending data, and supposed our serial port is running at 38400 bps. This means that we should have at least 3840 bytes free in our buffer when the high water mark. is reached, otherwise we may lose incoming data if our buffer overflows.
The input buffer levels at which these flow control events occur are not something that can be altered. Instead, the recommended way of adjusting the precise times at which flow control events will occur is to alter the size of the receive buffer using the member function SetReceiveBufferLength()
. In the situation just described, we would need to set a receive buffer size of around 16K to guarantee no data loss .
(In practise, it may well be possible to get away with a smaller buffer than this even when the sending system takes one second to respond, as we will almost certainly be removing data from the buffer during the latency period. However, the precise calculations would clearly depend on the processing requirements of the receiving application.)
As well as the buffers in the serial port drivers, the components used to implement many RS232 ports include a small internal FIFO buffer which can be switched on or off. The presence of such an internal buffer is signified by the iFifo
byte in TCommCaps
being set, and the FIFO can be enabled or disabled by setting or clearing the iFifo
byte in TCommConfig
as appropriate. The default state is for any FIFOs to be automatically enabled, as even a 16 byte FIFO (the usual size) significantly reduces the load on the processor during serial i/o operations, and applications should not disable this without good reason.
Software flow control presents particular problems of data transparency, since the characters used to suspend and resume the data flow may also occur as valid data bytes. There is no ideal method of handling this situation. For this reason, hardware flow control is the preferred handshaking method whenever there is a requirement for the transfer of binary data in either direction.
Different policies are adopted for solving data transparency issues, depending on the direction in which the handshaking operates.
Enabling software flow control when sending (setting the KConfigObeyXoff
bit in iHandshake
) doesn’t affect the data we are transmitting but it can affect the data we are receiving. In this respect, it is non-transparent. When either of the flow control characters are received, they are acted on by the serial drivers as required and are not passed through to the controlling application. This presents no problem if the serial port is being used to send data to a printer which implements XON/XOFF handshaking, but will most certainly cause difficulty if we are trying to receive binary data using a modem.
In contrast, enabling software flow control only when receiving (setting just the KConfigSendXoff
bit in iHandshake
) has no effect at all on data transferred in either direction. While it doesn’t prevent an application either receiving or transmitting XON or XOFF, you should note that the meaning of flow control characters embedded in binary data being sent is not going to be clear.
It is possible to change the characters that are used for software flow control with the TCommConfig
package simply by changing the values in iXonChar
and iXoffChar
. However, you would normally need compelling reasons to change the defaults of the ASCII DC1 (decimal 17) for XON and DC3 (decimal 19) for XOFF respectively. In particular, you should note that disabling flow control must be done through iHandshake
. Setting iXonChar
and iXoffChar
to zero is neither necessary nor desirable, and is liable to have extremely unpredictable effects.
The next three of the handshaking bitmasks are all of the form KConfigFailxxx
, and can be used to set up the conditions under which pending data transfers are automatically cancelled. Examples of the sort of situation this is designed to handle would be attempts to send data over a cable that had been pulled out, or disconnection of a modem during a file download.
An RComm
i/o request can be set to terminate early with a KErrCommsLineFail
error if any specified combination of the control lines fail. All you need to do is set the appropriate handshaking bits defined as KConfigFailCTS
, KConfigFailDSR
, and KConfigFailDCD
. If multiple failure conditions are enabled, it may be necessary for an application to use the Signals()
function to ascertain what the exact reason for the comms line failure might have been.
As with the speed and data format, it is possible to use Caps()
to find out whether a specified handshaking mode is supported or not. A matching set of enumerated bitmasks can be used with the iHandshake
byte in TCommCaps
to isolate specific capabilities of the port.