[PATCH v1] 8250: add support for DTR/DSR hardware flow control

From: Aristeu Rozanski
Date: Wed Aug 06 2008 - 17:24:56 EST


This patch adds support for DTR/DSR hardware flow control on 8250 driver on x86
machines. It's done by adding a CDTRDSR flag to work just like CRTSCTS, which is
not done on other architectures on purpose (so each maintainer can allocate it).

This patch was tested with success with a serial printer configured with a small
buffer and DTR/DSR flow control.

This is based on the work of Michael Westermann (http://lkml.org/lkml/2007/8/31/133)

Comments more than welcome.

Signed-off-by: Aristeu Rozanski <arozansk@xxxxxxxxxx>

---
drivers/serial/8250.c | 12 ++++++++--
drivers/serial/serial_core.c | 42 ++++++++++++++++++++++++++++++++++-
include/asm-x86/termbits.h | 1
include/linux/serial_core.h | 51 +++++++++++++++++++++++++++----------------
include/linux/termios.h | 5 ++++
5 files changed, 90 insertions(+), 21 deletions(-)

--- linus-2.6.orig/drivers/serial/8250.c 2008-08-05 16:44:26.000000000 -0400
+++ linus-2.6/drivers/serial/8250.c 2008-08-05 18:45:01.000000000 -0400
@@ -1409,7 +1409,7 @@ static unsigned int check_modem_status(s
if (status & UART_MSR_TERI)
up->port.icount.rng++;
if (status & UART_MSR_DDSR)
- up->port.icount.dsr++;
+ uart_handle_dsr_change(&up->port, status & UART_MSR_DSR);
if (status & UART_MSR_DDCD)
uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
if (status & UART_MSR_DCTS)
@@ -1739,9 +1739,17 @@ static inline void wait_for_xmitr(struct
unsigned int tmout;
for (tmout = 1000000; tmout; tmout--) {
unsigned int msr = serial_in(up, UART_MSR);
+ struct uart_info *info = up->port.info;
+
up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
- if (msr & UART_MSR_CTS)
+
+ if ((info->flags & UIF_CTS_FLOW) &&
+ (msr & UART_MSR_CTS))
break;
+ else if ((info->flags & UIF_DSR_FLOW) &&
+ (msr & UART_MSR_DSR))
+ break;
+
udelay(1);
touch_nmi_watchdog();
}
--- linus-2.6.orig/drivers/serial/serial_core.c 2008-08-05 16:44:26.000000000 -0400
+++ linus-2.6/drivers/serial/serial_core.c 2008-08-05 18:45:01.000000000 -0400
@@ -194,6 +194,13 @@ static int uart_startup(struct uart_stat
spin_unlock_irq(&port->lock);
}

+ if (info->flags & UIF_DSR_FLOW) {
+ spin_lock_irq(&port->lock);
+ if (!(port->ops->get_mctrl(port) & TIOCM_DSR))
+ info->port.tty->hw_stopped = 1;
+ spin_unlock_irq(&port->lock);
+ }
+
info->flags |= UIF_INITIALIZED;

clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
@@ -448,6 +455,11 @@ uart_change_speed(struct uart_state *sta
else
state->info->flags &= ~UIF_CTS_FLOW;

+ if (termios->c_cflag & CDTRDSR)
+ state->info->flags |= UIF_DSR_FLOW;
+ else
+ state->info->flags &= ~UIF_DSR_FLOW;
+
if (termios->c_cflag & CLOCAL)
state->info->flags &= ~UIF_CHECK_CD;
else
@@ -611,6 +623,8 @@ static void uart_throttle(struct tty_str

if (tty->termios->c_cflag & CRTSCTS)
uart_clear_mctrl(state->port, TIOCM_RTS);
+ if (tty->termios->c_cflag & CDTRDSR)
+ uart_clear_mctrl(state->port, TIOCM_DTR);
}

static void uart_unthrottle(struct tty_struct *tty)
@@ -627,6 +641,8 @@ static void uart_unthrottle(struct tty_s

if (tty->termios->c_cflag & CRTSCTS)
uart_set_mctrl(port, TIOCM_RTS);
+ if (tty->termios->c_cflag & CDTRDSR)
+ uart_set_mctrl(port, TIOCM_DTR);
}

static int uart_get_info(struct uart_state *state,
@@ -1212,6 +1228,9 @@ static void uart_set_termios(struct tty_
if (!(cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags))
mask |= TIOCM_RTS;
+ if (!(cflag & CDTRDSR) ||
+ !test_bit(TTY_THROTTLED, &tty->flags))
+ mask &= ~TIOCM_DTR;
uart_set_mctrl(state->port, mask);
}

@@ -1232,6 +1251,24 @@ static void uart_set_termios(struct tty_
}
spin_unlock_irqrestore(&state->port->lock, flags);
}
+
+ /* Handle turning off CDTRDSR */
+ if ((old_termios->c_cflag & CDTRDSR) && !(cflag & CDTRDSR)) {
+ spin_lock_irqsave(&state->port->lock, flags);
+ tty->hw_stopped = 0;
+ __uart_start(tty);
+ spin_unlock_irqrestore(&state->port->lock, flags);
+ }
+
+ if (!(old_termios->c_cflag & CDTRDSR) && (cflag & CDTRDSR)) {
+ spin_lock_irqsave(&state->port->lock, flags);
+ if (!(state->port->ops->get_mctrl(state->port) & TIOCM_DSR)) {
+ tty->hw_stopped = 1;
+ state->port->ops->stop_tx(state->port);
+ }
+ spin_unlock_irqrestore(&state->port->lock, flags);
+ }
+
#if 0
/*
* No need to wake up processes in open wait, since they
@@ -1511,7 +1548,8 @@ uart_block_til_ready(struct file *filp,
* not set RTS here - we want to make sure we catch
* the data from the modem.
*/
- if (info->port.tty->termios->c_cflag & CBAUD)
+ if (info->port.tty->termios->c_cflag & CBAUD &&
+ !(info->port.tty->termios->c_cflag & CDTRDSR))
uart_set_mctrl(port, TIOCM_DTR);

/*
@@ -1959,6 +1997,8 @@ uart_set_options(struct uart_port *port,

if (flow == 'r')
termios.c_cflag |= CRTSCTS;
+ if (flow == 'd')
+ termios.c_cflag |= CDTRDSR;

/*
* some uarts on other side don't support no flow control.
--- linus-2.6.orig/include/asm-x86/termbits.h 2008-08-05 16:44:26.000000000 -0400
+++ linus-2.6/include/asm-x86/termbits.h 2008-08-06 13:33:57.000000000 -0400
@@ -157,6 +157,7 @@ struct ktermios {
#define B3500000 0010016
#define B4000000 0010017
#define CIBAUD 002003600000 /* input baud rate */
+#define CDTRDSR 004000000000 /* DTR/DSR flow control */
#define CMSPAR 010000000000 /* mark or space (stick) parity */
#define CRTSCTS 020000000000 /* flow control */

--- linus-2.6.orig/include/linux/serial_core.h 2008-08-05 16:44:26.000000000 -0400
+++ linus-2.6/include/linux/serial_core.h 2008-08-05 18:45:01.000000000 -0400
@@ -351,6 +351,7 @@ struct uart_info {
*
* FIXME: use the ASY_ definitions
*/
+#define UIF_DSR_FLOW ((__force uif_t) (1 << 24))
#define UIF_CHECK_CD ((__force uif_t) (1 << 25))
#define UIF_CTS_FLOW ((__force uif_t) (1 << 26))
#define UIF_NORMAL_ACTIVE ((__force uif_t) (1 << 29))
@@ -505,34 +506,48 @@ uart_handle_dcd_change(struct uart_port
}

/**
- * uart_handle_cts_change - handle a change of clear-to-send state
+ * uart_handle_flow_control_change - handle a change of CTS or DSR
* @port: uart_port structure for the open port
- * @status: new clear to send status, nonzero if active
+ * @status: new CTS/DTR status, nonzero if active
*/
static inline void
-uart_handle_cts_change(struct uart_port *port, unsigned int status)
+uart_handle_flow_control_change(struct uart_port *port, unsigned int status)
{
struct uart_info *info = port->info;
struct tty_struct *tty = info->port.tty;

- port->icount.cts++;
-
- if (info->flags & UIF_CTS_FLOW) {
- if (tty->hw_stopped) {
- if (status) {
- tty->hw_stopped = 0;
- port->ops->start_tx(port);
- uart_write_wakeup(port);
- }
- } else {
- if (!status) {
- tty->hw_stopped = 1;
- port->ops->stop_tx(port);
- }
+ if (tty->hw_stopped) {
+ if (status) {
+ tty->hw_stopped = 0;
+ port->ops->start_tx(port);
+ uart_write_wakeup(port);
+ }
+ } else {
+ if (!status) {
+ tty->hw_stopped = 1;
+ port->ops->stop_tx(port);
}
}
}

+static inline void
+uart_handle_cts_change(struct uart_port *port, unsigned int status)
+{
+ struct uart_info *info = port->info;
+ port->icount.cts++;
+ if (info->flags & UIF_CTS_FLOW)
+ uart_handle_flow_control_change(port, status);
+}
+
+static inline void
+uart_handle_dsr_change(struct uart_port *port, unsigned int status)
+{
+ struct uart_info *info = port->info;
+ port->icount.dsr++;
+ if (info->flags & UIF_DSR_FLOW)
+ uart_handle_flow_control_change(port, status);
+}
+
#include <linux/tty_flip.h>

static inline void
@@ -556,7 +571,7 @@ uart_insert_char(struct uart_port *port,
* UART_ENABLE_MS - determine if port should enable modem status irqs
*/
#define UART_ENABLE_MS(port,cflag) ((port)->flags & UPF_HARDPPS_CD || \
- (cflag) & CRTSCTS || \
+ (cflag) & (CRTSCTS | CDTRDSR) || \
!((cflag) & CLOCAL))

#endif
--- linus-2.6.orig/include/linux/termios.h 2008-08-05 16:44:26.000000000 -0400
+++ linus-2.6/include/linux/termios.h 2008-08-05 18:45:01.000000000 -0400
@@ -4,4 +4,9 @@
#include <linux/types.h>
#include <asm/termios.h>

+#ifndef CDTRDSR
+#warning This architecture should implement CDTRDSR
+#define CDTRDSR 0 /* remove this when all architectures have a definition */
+#endif
+
#endif
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/