UART tx flow control

In the Linux kernel, the tty driver interface for UART devices is abstracted by the serial core API. The serial core supplies the tty driver implementation and, in turn, defines a set of control methods to be implemented by the UART driver. The control methods, along with other details of the serial core API, are defined in include/linux/serial_core.h  and briefly documented in Documentation/serial/driver .

The UART driver methods used by the serial core to manage output flow control are:

void start_tx(struct uart_port *)
void stop_tx(struct uart_port *)

Simple enough.

If only that were true. While stop_tx() is straightforward to implement, the serial core leaves much to the UART driver to determine what action(s) should be taken when the start_tx() method is called. The table below summarizes the appropriate action(s).

Condition Action(s)
port->x_char != 0 Transmit the 1-byte value in x_char, regardless of any other state. Only required if the UART driver does not define a send_xchar() method.
uart_tx_stopped(port) Flow is stopped. Do not transmit data in the tx ring buffer. Turn off the transmitter or, at least, disable further tx interrupts.
uart_circ_empty(port) No data to transmit; disable further tx interrupts.

Details

The serial core does not maintain or track the ‘transmitter enabled’ state. In practice, this means the start_tx()/stop_tx() interface is asymmetric; that is, the serial core calls start_tx() if the transmitter may need to be on and only calls stop_tx() if the transmitter should be off.

The serial core also expects the UART driver to automatically turn off the transmitter (or disable further tx ints) if conditions dictate, such as if the tx ring buffer is empty. For example, the 8250 UART driver evaluates the conditions and auto-stops transmission from its interrupt handler:

serial8250_default_handle_irq
  serial8250_handle_irq
    ...
    if (transmitter holding register empty)
      serial8520_tx_chars
        if (port->x_char != 0)
          transmit x_char
          return
        if (uart_tx_stopped(port))
          stop transmitter
          return
        if (uart_circ_empty(xmit))
          disable further THRE ints
          return
        transmit data
        if (uart_circ_empty(xmit))
          disable further THRE ints

As of Linux kernel v3.15, the serial core calls start_tx() even if there is no data in the tx ring buffer. This is a bug fix to accommodate hardware which does actually stop the transmitter in its stop_tx() method (rather than just disabling the THRE interrupt), and thus may have an empty ring buffer while data may still be in the transmitter. Seth Bollinger points out,

Any window, however small, could leave bytes stuck in the transmitter
forever — particularly if there will be no further transmission until
receiving a response.

Out-of-tree UART drivers should be reviewed to verify that transmit is not attempted if the tx ring buffer is empty (other than for sending x_char).

Handling x_char

If the UART driver does not define a send_xchar() method, the UART driver’s start_tx() method must cause the byte value in port->x_char to be transmitted, if non-zero. After the x_char is sent, port->x_char must be reset to 0. The UART driver should strive to send the x_char as soon as possible, preferably before any data already in the tx ring buffer. If possible, DMA drivers should stop in-progress DMA, transmit the x_char, and restart DMA. The reference UART driver, amba-pl011.c , uses this scheme when operating in DMA mode.

x_char must be sent regardless of the flow control state. Conversely, sending x_char must not cause data in the tx ring buffer to be sent if flow is off; ie., if uart_tx_stopped(port). Sending x_char should also not cause a write wakeup.

A standalone unit test for a UART driver’s x_char handling is available for download below. Instructions for compiling, using, and diagnosing are included in the file.

License Filename Size Hash  
MIT tty_xchar.c 11KB MD5 8f324fd8a1f800ef666dbe11e2f0a91b
SHA-1 5ad637a29d8ccd00ffee66ae54f3f8d88332c0a2
Download 

Comments