public inbox for linux-serial@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1] 8250: add support for DTR/DSR hardware flow control
@ 2008-08-06 21:14 Aristeu Rozanski
  2008-08-07  8:32 ` Laurent Pinchart
  0 siblings, 1 reply; 11+ messages in thread
From: Aristeu Rozanski @ 2008-08-06 21:14 UTC (permalink / raw)
  To: linux-serial; +Cc: linux-kernel

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@redhat.com>

---
 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

^ permalink raw reply	[flat|nested] 11+ messages in thread
* Re: [PATCH v1] 8250: add support for DTR/DSR hardware flow control
@ 2008-08-29 15:33 Christopher Gibson
  2008-09-01  8:23 ` Tosoni
  0 siblings, 1 reply; 11+ messages in thread
From: Christopher Gibson @ 2008-08-29 15:33 UTC (permalink / raw)
  To: linux-serial

I've had a look at the asm-cris rs485 implementation, as JP Tosoni
suggested, and say that that interface would satisfy my requirements,
and would also provide a suitable interface for controlling out dated
radio modems.  I would say though that some additional consideration
should be given to the interface.

I had assumed that the rts_on_send, and rts_after_send were time delay
figures until I read the suggested rework by Alan Cox, then read through
the crisv10.c code.  In the crisv10 serial driver code the rts_on_send
specifies the state that the RTS line should change to when a call to
the TIOCSERWRRS485 ioctl is made the rts_after_send is the state that
RTS is changed to after the transmission has completed.  So in two of
the four possible combinations user space control of RTS is still
required in order to get any directional control activity out of the RTS
line.  I would be so bold as to say that this combination of user /
automatic direction control would be unlikely to be ever used by anyone.
If this is correct then the two flags could be replaced with one
rts_tx_level indicating if RTS should be high or low during a
transmission, and pre-transmission delay.  Would this be clearer?

The other observation about the crsv10.c implementation is that the call
to TIOCSERWRRS485 ioctl, or write() if the enabled flag is set, does not
return until the transmission has completed and the line has been turned
around.  This functionality could be restrictive in some situations
(single threaded user applications that wish to do other stuff during
transmission).

My suggestion would be to scrap the TIOCSERWRRS485 ioctl, in favour of
using the standard write(fd) and drain(fd) function calls, but to stick
with the TIOCSERSETRS485 ioctl for automatic direction control port
setup.  I would be suggesting a control structure (based on Alan Cox's
suggested modifications of the include/asm-cris/rs485.h) as follows:

/*
 * Serial interface for controlling RS485 settings on chips with suitable
 * support. Set with TIOCSRS485 and get with TIOCGRS485 if supported by your
 * platform. The set function returns the new state, with any unsupported bits
 * reverted appropriately.
 */

struct serial_rs485 {
	__u32	flags;			/* RS485 feature flags */
#define SER_RS485_MODES			(7 << 0)	/* Mask for mode bits. */
#define SER_RS485_MODE_DISABLED		(0 << 0)
#define SER_RS485_MODE_RTS_TX_HIGH	(2 << 0)
#define SER_RS485_MODE_RTS_TX_LOW	(3 << 0)
#define SER_RS485_MODE_DTR_TX_HIGH	(4 << 0)
#define SER_RS485_MODE_DTR_TX_LOW	(5 << 0)
	__u16	delay_rts_before_send;	/* Milliseconds */
	__u16	delay_rts_after_send;	/* Milliseconds */
	__u32	padding[6];		/* Memory is cheap, new structs
					   are a royal PITA .. */
};

Any comments?

The above would allow for RTS and DTR direction control as well as
settings for lead in and lead out timing.

-- 
Christopher Gibson <chris@toftronix.com.au>


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2008-09-01  8:25 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-06 21:14 [PATCH v1] 8250: add support for DTR/DSR hardware flow control Aristeu Rozanski
2008-08-07  8:32 ` Laurent Pinchart
2008-08-07  9:14   ` Alan Cox
2008-08-07 12:54   ` Tosoni
2008-08-13 11:39     ` Alan Cox
2008-08-18 15:25     ` Alan Cox
2008-08-20 21:43       ` Aristeu Rozanski
2008-08-21 10:23         ` Alan Cox
2008-08-21 18:59           ` 'Aristeu Rozanski'
  -- strict thread matches above, loose matches on Subject: below --
2008-08-29 15:33 Christopher Gibson
2008-09-01  8:23 ` Tosoni

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox