Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 89877e42827f16fa5f86b1df0c2860b1 > files > 2295

kernel-2.6.18-128.1.10.el5.src.rpm

From: Aristeu Rozanski <aris@redhat.com>
Date: Tue, 2 Sep 2008 15:37:17 -0400
Subject: [serial] 8250: support for DTR/DSR hardware flow control
Message-id: 20080902193717.GG10700@redhat.com
O-Subject: [RHEL5.3 PATCH 2/2] 8250: add support for DTR/DSR hardware flow control v3
Bugzilla: 445215
RH-Acked-by: Mauro Carvalho Chehab <mchehab@redhat.com>

https://bugzilla.redhat.com/show_bug.cgi?id=445215

8250: add support for DTR/DSR hardware flow control

This patch adds support for DTR/DSR hardware flow control to 8250
driver. The upstream version will differ from this one, since it'll
take a bigger rework.

Tested with success by the customer and by me on a serial P.O.S. printer.

diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index ce6dede..c5e7cfb 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -1320,7 +1320,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up)
 		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)
@@ -1629,8 +1629,20 @@ static inline void wait_for_xmitr(struct uart_8250_port *up, int bits)
 
 	/* Wait up to 1s for flow control if necessary */
 	if (up->port.flags & UPF_CONS_FLOW) {
+		struct uart_info *info = up->port.info;
+		unsigned int msr;
+
 		tmout = 1000000;
-		while (!(serial_in(up, UART_MSR) & UART_MSR_CTS) && --tmout) {
+		while (--tmout) {
+			msr = serial_in(up, UART_MSR);
+
+			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();
 		}
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index fef5991..5dc8fce 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -199,6 +199,13 @@ static int uart_startup(struct uart_state *state, int init_hw)
 			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->tty->hw_stopped = 1;
+			spin_unlock_irq(&port->lock);
+		}
+
 		info->flags |= UIF_INITIALIZED;
 
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
@@ -591,6 +598,8 @@ static void uart_throttle(struct tty_struct *tty)
 
 	if (tty->termios->c_cflag & CRTSCTS)
 		uart_clear_mctrl(state->port, TIOCM_RTS);
+	if (state->info->flags & UIF_DSR_FLOW)
+		uart_clear_mctrl(state->port, TIOCM_DTR);
 }
 
 static void uart_unthrottle(struct tty_struct *tty)
@@ -607,6 +616,8 @@ static void uart_unthrottle(struct tty_struct *tty)
 
 	if (tty->termios->c_cflag & CRTSCTS)
 		uart_set_mctrl(port, TIOCM_RTS);
+	if (state->info->flags & UIF_DSR_FLOW)
+		uart_set_mctrl(port, TIOCM_DTR);
 }
 
 static int uart_get_info(struct uart_state *state,
@@ -1168,7 +1179,10 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios
 		if (!(cflag & CRTSCTS) ||
 		    !test_bit(TTY_THROTTLED, &tty->flags))
 			mask |= TIOCM_RTS;
-		uart_set_mctrl(state->port, mask);
+		if (state->info->flags & UIF_DSR_FLOW)
+			mask &= ~TIOCM_DTR;
+		if (mask)
+			uart_set_mctrl(state->port, mask);
 	}
 
 	/* Handle turning off CRTSCTS */
@@ -1182,6 +1196,7 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios
 	/* Handle turning on CRTSCTS */
 	if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
 		spin_lock_irqsave(&state->port->lock, flags);
+		state->port->info->flags &= ~UIF_DSR_FLOW;
 		if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) {
 			tty->hw_stopped = 1;
 			state->port->ops->stop_tx(state->port);
@@ -1202,6 +1217,81 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios
 #endif
 }
 
+static void uart_set_termiox(struct tty_struct *tty, struct termiox *tnew)
+{
+	struct uart_state *state = tty->driver_data;
+	long flags;
+
+	if ((tnew->x_cflag & (CTSXON | RTSXOFF)) == (CTSXON | RTSXOFF)) {
+		/* Handle turning on CRTSCTS */
+		spin_lock_irqsave(&state->port->lock, flags);
+		tty->termios->c_cflag |= CRTSCTS;
+		state->port->info->flags &= ~UIF_DSR_FLOW;
+		state->port->info->flags |= UIF_CTS_FLOW;
+
+		if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) {
+			tty->hw_stopped = 1;
+			state->port->ops->stop_tx(state->port);
+		} else if (tty->hw_stopped) {
+			tty->hw_stopped = 0;
+			__uart_start(tty);
+		}
+		spin_unlock_irqrestore(&state->port->lock, flags);
+
+		uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR);
+
+		if (tty->termios->c_cflag & CBAUD) {
+			unsigned int mask = TIOCM_DTR;
+
+			if (!test_bit(TTY_THROTTLED, &tty->flags))
+				mask |= TIOCM_RTS;
+			uart_set_mctrl(state->port, mask);
+		}
+	} else if ((tnew->x_cflag & (DSRXON | DTRXOFF)) == (DSRXON | DTRXOFF)) {
+		spin_lock_irqsave(&state->port->lock, flags);
+		tty->termios->c_cflag &= ~CRTSCTS;
+		state->port->info->flags &= ~UIF_CTS_FLOW;
+		state->port->info->flags |= UIF_DSR_FLOW;
+
+		if (!(state->port->ops->get_mctrl(state->port) & TIOCM_DSR)) {
+			tty->hw_stopped = 1;
+			state->port->ops->stop_tx(state->port);
+		} else if (tty->hw_stopped) {
+			tty->hw_stopped = 0;
+			__uart_start(tty);
+		}
+		spin_unlock_irqrestore(&state->port->lock, flags);
+
+		uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR);
+
+		if (tty->termios->c_cflag & CBAUD)
+			uart_set_mctrl(state->port, TIOCM_DTR);
+	} else if (!(tnew->x_cflag & (CTSXON | RTSXOFF | DSRXON | DTRXOFF))) {
+		/* disabling flow control */
+		spin_lock_irqsave(&state->port->lock, flags);
+		tty->termios->c_cflag &= ~CRTSCTS;
+		state->port->info->flags &= ~UIF_CTS_FLOW;
+		state->port->info->flags &= ~UIF_DSR_FLOW;
+
+		tty->hw_stopped = 0;
+		__uart_start(tty);
+		spin_unlock_irqrestore(&state->port->lock, flags);
+
+		uart_clear_mctrl(state->port, TIOCM_RTS);
+		uart_set_mctrl(state->port, TIOCM_DTR);
+	}
+}
+
+static void uart_get_termiox(struct tty_struct *tty, struct termiox *t)
+{
+	struct uart_state *state = tty->driver_data;
+
+	if (state->info->flags & UIF_CTS_FLOW)
+		t->x_cflag = CTSXON | RTSXOFF;
+	else if (state->info->flags & UIF_DSR_FLOW)
+		t->x_cflag = DSRXON | DTRXOFF;
+}
+
 /*
  * In 2.4.5, calls to this will be serialized via the BKL in
  *  linux/drivers/char/tty_io.c:tty_release()
@@ -1468,7 +1558,8 @@ uart_block_til_ready(struct file *filp, struct uart_state *state)
 		 * not set RTS here - we want to make sure we catch
 		 * the data from the modem.
 		 */
-		if (info->tty->termios->c_cflag & CBAUD)
+		if (info->tty->termios->c_cflag & CBAUD &&
+		    !(info->flags & UIF_DSR_FLOW))
 			uart_set_mctrl(port, TIOCM_DTR);
 
 		/*
@@ -2180,9 +2271,11 @@ int uart_register_driver(struct uart_driver *drv)
 	normal->subtype		= SERIAL_TYPE_NORMAL;
 	normal->init_termios	= tty_std_termios;
 	normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-	normal->flags		= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	normal->flags		= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HAS_TERMIOX;
 	normal->driver_state    = drv;
 	tty_set_operations(normal, &uart_ops);
+	normal->set_termiox = uart_set_termiox;
+	normal->get_termiox = uart_get_termiox;
 
 	/*
 	 * Initialise the UART state(s).
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index f9fdf97..3fad04f 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -315,6 +315,7 @@ struct uart_info {
  * Definitions for info->flags.  These are _private_ to serial_core, and
  * are specific to this structure.  They may be queried by low level drivers.
  */
+#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))
@@ -474,34 +475,48 @@ uart_handle_dcd_change(struct uart_port *port, unsigned int status)
 }
 
 /**
- *	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->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
@@ -526,6 +541,7 @@ uart_insert_char(struct uart_port *port, unsigned int status,
  */
 #define UART_ENABLE_MS(port,cflag)	((port)->flags & UPF_HARDPPS_CD || \
 					 (cflag) & CRTSCTS || \
+					 ((port)->info && ((port)->info->flags & UIF_DSR_FLOW)) || \
 					 !((cflag) & CLOCAL))
 
 #endif