Skip to contentSkip to conversation

Suggested patch for linux

PATCH Axel Hosemann 7 participants 11 messages Jul 10, 2008 – Aug 4, 2008 2 messages not in the archive

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

Dear Sirs,
the support of the serial interface by linux systems nowadays suffers from the disadvantage, that hardware handshake is only supported for the full duplex mode. That means, that the RTS signal is getting always asserted immediately after the serial was opened. So the RTS signal under linux, as far as I understood it, carries exactly the same information as the DTR line. That means, that there is no half-duplex behaviour provided by linux. A half-duplex application as e.g. RS-485 controllers or radio modems are depending on a different behaviour: The serial port asserts DTR after it was opened, but RTS must only be asserted, when data has been written to the tx buffer for transmission. Stimulated by the RTS signal, the DCE will then engage the channel for transmission and release it, after the RTS has been deasserted when the buffer had been sent. This behaviour is up to now not supported by the linux serial drivers. Therefore I make a suggestion for adding the half-duplex support for linux kernels by the attached patch, which I created and tested on a UART 16550 compliant hardware and additionally by means of a ftdi usb to serial port adaptor. The patch was compiled and tested with an UBUNTU 2.6.24-19-generic environment.
If you see a chance, to add this functionality for future linux kernels, I would be interested in further discussions about this patch.

Regards

Axel Hosemann
Bergstrasse 6f
CH 5644 Auw

drivers/serial/serial_core.c +92 -3
diff -urN linux-2.6.24/drivers/serial/serial_core.c linux-2.6.24-new/drivers/serial/serial_core.c
@@ -75,6 +75,9 @@
 	 */
 	BUG_ON(!info);
 	tasklet_schedule(&info->tlet);
+#ifdef RTSHDX
+	wake_up_interruptible(&port->rts_wait);
+#endif
 }
 
 static void uart_stop(struct tty_struct *tty)
@@ -181,8 +184,15 @@
 			 * Setup the RTS and DTR signals once the
 			 * port is open and ready to respond.
 			 */
-			if (info->tty->termios->c_cflag & CBAUD)
+			if (info->tty->termios->c_cflag & CBAUD){
+#ifdef RTSHDX
+				port->toggleRTS = false;
+				uart_set_mctrl(port, TIOCM_DTR);
+				uart_clear_mctrl(port, TIOCM_RTS);
+#else
 				uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
+#endif
+			}
 		}
 
 		if (info->flags & UIF_CTS_FLOW) {
@@ -473,6 +483,47 @@
 	uart_start(tty);
 }
 
+#ifdef RTSHDX
+static void uart_wait_infinite_until_sent(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->port;
+	unsigned long char_time;
+
+	BUG_ON(!kernel_locked());
+
+	char_time = (port->timeout - HZ/50) / port->fifosize;
+	char_time = char_time / 5;
+	if (char_time == 0)
+		char_time = 1;
+	
+	if (port->type == PORT_UNKNOWN || port->fifosize == 0)
+		return;
+
+	/*
+	 * Check whether the transmitter is empty every 'char_time'.
+	 */
+	while (!port->ops->tx_empty(port)) {
+		msleep_interruptible(jiffies_to_msecs(char_time));
+	}
+	//wait again until shift register is empty
+	msleep_interruptible(jiffies_to_msecs(char_time));
+	set_current_state(TASK_RUNNING); /* might not be needed */
+}
+
+static void simplified_sleep_on(wait_queue_head_t *queue)
+{
+	wait_queue_t wait;
+
+	init_waitqueue_entry(&wait, current);
+	current->state = TASK_INTERRUPTIBLE;
+
+	add_wait_queue(queue, &wait);
+	schedule();
+	remove_wait_queue (queue, &wait);
+}
+#endif
+
 static int
 uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
 {
@@ -497,6 +548,9 @@
 	if (!circ->buf)
 		return 0;
 
+#ifdef RTSHDX
+	uart_set_mctrl(port, TIOCM_RTS);
+#endif
 	spin_lock_irqsave(&port->lock, flags);
 	while (1) {
 		c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
@@ -513,6 +567,16 @@
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	uart_start(tty);
+#ifdef RTSHDX
+	if(port->toggleRTS){
+		//first wait until the device indicates transmission started
+		simplified_sleep_on(&port->rts_wait);
+		//second wait until the device indicates transmission done
+		uart_wait_infinite_until_sent(tty);
+		//now we are ready to deassert RTS
+		uart_clear_mctrl(port, TIOCM_RTS);
+	}
+#endif	
 	return ret;
 }
 
@@ -1143,6 +1207,15 @@
 	unsigned int cflag = tty->termios->c_cflag;
 
 	BUG_ON(!kernel_locked());
+#ifdef RTSHDX
+	state->port->toggleRTS = tty->termios->c_lflag & RTSHDX ? true : false;
+	if(state->port->toggleRTS){
+		uart_clear_mctrl(state->port, TIOCM_RTS);
+	}
+	else {
+		uart_set_mctrl(state->port, TIOCM_RTS);
+	}
+#endif	
 
 	/*
 	 * These are the bits that are used to setup various
@@ -1167,8 +1240,14 @@
 	if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
 		unsigned int mask = TIOCM_DTR;
 		if (!(cflag & CRTSCTS) ||
-		    !test_bit(TTY_THROTTLED, &tty->flags))
+		    !test_bit(TTY_THROTTLED, &tty->flags)){
+#ifdef RTSHDX
+			if(!state->port->toggleRTS)
+				mask |= TIOCM_RTS;
+#else	
 			mask |= TIOCM_RTS;
+#endif	
+		}
 		uart_set_mctrl(state->port, mask);
 	}
 
@@ -1413,8 +1492,15 @@
 		/*
 		 * And finally enable the RTS and DTR signals.
 		 */
-		if (tty->termios->c_cflag & CBAUD)
+		if (tty->termios->c_cflag & CBAUD){
+#ifdef RTSHDX
+			//uart_update_mctrl(port,TIOCM_DTR,TIOCM_RTS);
+			uart_clear_mctrl(port, TIOCM_RTS);
+			uart_set_mctrl(port, TIOCM_DTR);
+#else
 			uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+#endif
+		}		
 	}
 }
 
@@ -1527,6 +1613,9 @@
 		if (state->info) {
 			init_waitqueue_head(&state->info->open_wait);
 			init_waitqueue_head(&state->info->delta_msr_wait);
+#ifdef RTSHDX
+			init_waitqueue_head(&state->port->rts_wait);
+#endif
 
 			/*
 			 * Link the info into the other structures.
drivers/usb/serial/ftdi_sio.c +121 -8

220 lines not on this page · raw message

include/asm-x86/termbits.h +1 -0
diff -urN linux-2.6.24/include/asm-x86/termbits.h linux-2.6.24-new/include/asm-x86/termbits.h
@@ -176,6 +176,7 @@
 #define ECHOPRT	0002000
 #define ECHOKE	0004000
 #define FLUSHO	0010000
+#define RTSHDX  0020000
 #define PENDIN	0040000
 #define IEXTEN	0100000
 
include/linux/serial_core.h +4 -0
diff -urN linux-2.6.24/include/linux/serial_core.h linux-2.6.24-new/include/linux/serial_core.h
@@ -297,6 +297,10 @@
 	unsigned char		suspended;
 	unsigned char		unused[2];
 	void			*private_data;		/* generic platform data pointer */
+#ifdef RTSHDX
+	bool toggleRTS;
+	wait_queue_head_t rts_wait; // Used for blocking write
+#endif	
 };
 
 /*
#2 lore raw
In reply to a message not in the archive
Quoted 4 lines

Stimulated by the RTS signal, the DCE will then engage the channel for transmission and release it, after the RTS has been deasserted when the buffer had been sent. This behaviour is up to now not supported by the linux serial drivers.

Actually you can drive this behaviour from user space which is what software like scarabd has done for many years.

Quoted 2 lines

If you see a chance, to add this functionality for future linux kernels, I would be interested in further discussions about this patch.

The big problem is that the kernel does not know what is a "transmission" it just sees a series of writes to the device. In many cases the multi-drop or radio systems also need the caller to wait for a transmission slot either by beacon, by timing or by monitoring the carrier detect to avoid collisions.

That seems to best be done in user space as the algorithms are quite variable and some are complex.

Do we really benefit from having this in kernel ?

Alan
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

#3 lore raw
Quoted 6 lines

The big problem is that the kernel does not know what is a "transmission" it just sees a series of writes to the device. In many cases the multi-drop or radio systems also need the caller to wait for a transmission slot either by beacon, by timing or by monitoring the carrier detect to avoid collisions.

I believe that he is referring to something much simpler than you are thinking. He is talking about 2-wire RS485 transmissions in which the RTS signal is used to enable the driver chips just before transmitting data and disabling (tri-stating) them just after transmitting data. There are some UARTs that have this behavior built into them.

Quoted 5 lines

That seems to best be done in user space as the algorithms are quite variable and some are complex.

Do we really benefit from having this in kernel ?

The problem that can come up when executing this feature in user-land (though not exactly common) is when the hardware on the other end responds to your message faster than your user app was able to detect that the UART is finished and then toggle RTS. When this happens both ends are trying to drive the line and you have bus contention, lost data and possibly damage to the driver chips themselves.

Matt Schulte
Commtech, Inc.
http://www.commtech-fastcom.com

#11 lore raw
In reply to Alan Cox
Quoted 6 lines · Alan Cox
Quoted 4 lines

Stimulated by the RTS signal, the DCE will then engage the channel for transmission and release it, after the RTS has been deasserted when the buffer had been sent. This behaviour is up to now not supported by the linux serial drivers.

Actually you can drive this behaviour from user space

That doesn't generally work. You often can't respond quickly enough in user space. You need to turn the line around within as little as a few bit-times. At high baud rates, you simply can't do that from user space (especially when the UART is attached via something like USB or Ethernet).

Quoted 8 lines

which is what software like scarabd has done for many years.

Quoted 3 lines

If you see a chance, to add this functionality for future linux kernels, I would be interested in further discussions about this patch.

The big problem is that the kernel does not know what is a "transmission" it just sees a series of writes to the device.

"Transmission" is when the UART is sending data (when the shift register is not empty).

Quoted 4 lines

In many cases the multi-drop or radio systems also need the caller to wait for a transmission slot either by beacon, by timing or by monitoring the carrier detect to avoid collisions.

That's obviously beyond the scope of the serial driver. Controlling RTS in half-duplex mode isn't.

Quoted 4 lines

That seems to best be done in user space as the algorithms are quite variable and some are complex.

Do we really benefit from having this in kernel ?

People who do industrial communications sure would. I maintain a couple serial drivers that support half-duplex mode exactly as described by the OP, and we have to control that feature via non-standard hacks.

In the past, I added half-duplex mode support into the standard Linux 16x50 driver, but could never get it to work reliably across various chipsets (PC motherboard UART designs don't all seem to set the shift register empty bit at the same time).

If there was at least a standard way to enable half-duplex mode, it would be easier to support on hardware that correctly implements RTS control for half-duplex operation.

Signature · 3 lines
Grant Edwards                   grante             Yow! Here we are in America
                                  at               ... when do we collect
                               visi.com            unemployment?
#4 lore raw
In reply to Matt Schulte

Dear Matt,
first of all, I must apologise for not being familiar with the mailing rules of your organisation. I already received a mail by a female developer, who was annoyed by me addressing her as "Dear Sirs". Oops! I'm sorry.
From your reply I see, that you understood the reason for the suggested patch: I try to port a half duplex application from Windows to Linux, which on one hand controls a radio modem by means of a 2 wire RS-485 bus, and on the other hand exchanges payload data over the half duplex radio channel across an asynchronous port. There is no chance for me doing that without this patch, because Linux does not support a feature similar to the Windows "RTS_CONTROL_TOGGLE" option.
To the problem you mentioned. Yes, your right: If the RS-485 slave device would reply a request with hardware timings within a few microseconds, a collision would occur. This would certainly not destroy the RS-485 chips, because they are generally protected against these collision situations. But theoretically the application would not work, when the slave would reply faster, than the PC could release the RTS signal. In practice, I did not face this situation (yet!) Probably because the receiving slave needs a micro controller itself, checking the checksum, interpreting the request and preparing the response. But the problem persists: The OS controlled deassertion of the RTS signal may be too slow for fast RS-485 slaves. Anyway, up to now, I had no problems with that issue with the mentioned Windows application. But when I'm back from my holydays I will do some measurements to compare the timing between the Windows application and the suggested Linux patch. For radio modem transmission, there will be no problem at all: Radio modems will always utilise FEC functionalities, which imply interleaving and deinterleaving delays. The deinterleaving delay of the receiver is in the range of hundreds of milliseconds. Even on legacy PC hardware platforms, the OS should be able to deassert RTS in time.
However, the half duplex feature could extend the Linux functionality. I read in a few forum threads, that there would be a demand for this functionality besides my personal interests.

Best Regards
Axel Hosemann

Quoted 21 lines · Matt Schulte
Quoted 6 lines

The big problem is that the kernel does not know what is a "transmission" it just sees a series of writes to the device. In many cases the multi-drop or radio systems also need the caller to wait for a transmission slot either by beacon, by timing or by monitoring the carrier detect to avoid collisions.

I believe that he is referring to something much simpler than you are thinking. He is talking about 2-wire RS485 transmissions in which the RTS signal is used to enable the driver chips just before transmitting data and disabling (tri-stating) them just after transmitting data. There are some UARTs that have this behavior built into them.

Quoted 5 lines

That seems to best be done in user space as the algorithms are quite variable and some are complex.

Do we really benefit from having this in kernel ?

The problem that can come up when executing this feature in user-land (though not exactly common) is when the hardware on the other end responds to your message faster than your user app was able to detect that the UART is finished and then toggle RTS. When this happens both ends are trying to drive the line and you have bus contention, lost data and possibly damage to the driver chips themselves.

Matt Schulte
Commtech, Inc.
http://www.commtech-fastcom.com

#5 lore raw
In reply to Matt Schulte

On Thu, 10 Jul 2008 09:16:40 -0500

Quoted 3 lines

I believe that he is referring to something much simpler than you are thinking. He is talking about 2-wire RS485 transmissions in which the RTS signal is used to enable the driver chips just before transmitting

Its all the same thing. And actually the radios are often RS485

Quoted 10 lines
Quoted 4 lines

That seems to best be done in user space as the algorithms are quite variable and some are complex.

Do we really benefit from having this in kernel ?

The problem that can come up when executing this feature in user-land (though not exactly common) is when the hardware on the other end responds to your message faster than your user app was able to detect that the UART is finished and then toggle RTS. When this happens both ends are trying to drive the line and you have bus contention, lost data and possibly damage to the driver chips themselves.

The chips are quite safe. Can we stick to real reasons.

In terms of performance you can manage RTS with a real time application if need be and that will give you very close to kernel side response time.

Alan

#6 lore raw
Quoted 8 lines
Quoted 7 lines
Quoted 1 line

Do we really benefit from having this in kernel ?

The problem that can come up when executing this feature in user-land (though not exactly common) is when the hardware on the other end responds to your message faster than your user app was able to detect that the UART is finished and then toggle RTS. When this happens both ends are trying to drive the line and you have bus contention, lost data and possibly damage to the driver chips themselves.

Quoted 6 lines

The chips are quite safe. Can we stick to real reasons.

In terms of performance you can manage RTS with a real time application if need be and that will give you very close to kernel side response time.

Alan

I apologize if this is a duplicate to anyone, but I've been trying to get my response through to the board and kept getting rejected, I think this one will make it through...

The chips may or may not be safe, that isn't the point. There are real world applications where response time is an issue and meeting these criteria is sometimes impossible from a user application. Switching to an RTOS is certainly an option, it would very likely allow you to manage RTS withing spec. However this might not be very practical for many users. I suppose another option is to switch to a serial card that uses a UART that has this capability built in. I don't want to sound like an advertisement, but several of my line of Fastcom serial cards have this feature, where the RS485 gating signal is governed autonomously by the UART itself.

Matt Schulte
Commtech, Inc.

#8 lore raw
In reply to a message not in the archive
Quoted 4 lines

The chips may or may not be safe, that isn't the point. There are real world applications where response time is an issue and meeting these criteria is sometimes impossible from a user application. Switching to an RTOS is certainly an option, it would very likely

Firstly: Linux has an RT patch and its expected to get merged soon
Second: Linux has RT processes already
Third: A non RT Linux kernel space and user space will not give much different performance

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

#9 lore raw
In reply to Matt Schulte
Quoted 11 lines · Matt Schulte
Quoted 6 lines

The big problem is that the kernel does not know what is a "transmission" it just sees a series of writes to the device. In many cases the multi-drop or radio systems also need the caller to wait for a transmission slot either by beacon, by timing or by monitoring the carrier detect to avoid collisions.

I believe that he is referring to something much simpler than you are thinking. He is talking about 2-wire RS485 transmissions in which the RTS signal is used to enable the driver chips just before transmitting data and disabling (tri-stating) them just after transmitting data. There are some UARTs that have this behavior built into them.

And that feature is currently not supported by Linux, even with UARTs that have hardware support for "Auto RS485" mode, such as the 8250-compatible XR16C2850.

The issue is that no flag is defined for the c_cflag field of the termios structure to enable that feature. The following patch is an example of what would be required.

-----------------------------------------------------------------------------

drivers/serial/8250.c +14 -0
index 62a2e49..a1351d5 100644
@@ -2071,6 +2071,20 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
 	if (up->port.type == PORT_16750)
 		serial_outp(up, UART_FCR, fcr);
 
+#ifdef CARTS
+	/* Auto RS485 Direction Control on 16850 UARTs */
+	if (up->port.type == PORT_16850) {
+		unsigned char fctr;
+
+		serial_outp(up, UART_LCR, 0xbf);
+		fctr = serial_inp(up, UART_FCTR) & ~UART_FCTR_RS485;
+		if (termios->c_cflag & CARTS)
+			fctr |= UART_FCTR_RS485;
+		serial_outp(up, UART_FCTR, fctr);
+		serial_outp(up, UART_LCR, 0);
+	}
+#endif
+
 	serial_outp(up, UART_LCR, cval);		/* reset DLAB */
 	up->lcr = cval;					/* Save LCR */
 	if (up->port.type != PORT_16750) {
include/asm-powerpc/termbits.h +1 -0
index 5e79198..7b7ee27 100644
@@ -166,6 +166,7 @@ struct ktermios {
 #define HUPCL	00040000
 
 #define CLOCAL	00100000
+#define CARTS	  004000000000		/* auto RTS control */
 #define CMSPAR	  010000000000		/* mark or space (stick) parity */
 #define CRTSCTS	  020000000000		/* flow control */
 
include/linux/serial_reg.h +1 -0
index 3c8a6aa..3db78cc 100644
@@ -188,6 +188,7 @@
 #define UART_FCTR_RTS_8DELAY	0x03
 #define UART_FCTR_IRDA		0x04  /* IrDa data encode select */
 #define UART_FCTR_TX_INT	0x08  /* Tx interrupt type select */
+#define UART_FCTR_RS485		0x08  /* Auto RS485 direction control */
 #define UART_FCTR_TRGA		0x00  /* Tx/Rx 550 trigger table select */
 #define UART_FCTR_TRGB		0x10  /* Tx/Rx 650 trigger table select */
 #define UART_FCTR_TRGC		0x20  /* Tx/Rx 654 trigger table select */

-----------------------------------------------------------------------------

As we need a new flag which isn't Posix-compliant as far as I know, the scope of this change can be broader than just the Linux kernel.

Would the above patch be acceptable (I'm open to changes to the CARTS name) as-is, or does it have to be discussed with userspace developers first ?

Quoted 10 lines
Quoted 4 lines

That seems to best be done in user space as the algorithms are quite variable and some are complex.

Do we really benefit from having this in kernel ?

The problem that can come up when executing this feature in user-land (though not exactly common) is when the hardware on the other end responds to your message faster than your user app was able to detect that the UART is finished and then toggle RTS. When this happens both ends are trying to drive the line and you have bus contention, lost data and possibly damage to the driver chips themselves.

Best regards,

Signature · 9 lines
Laurent Pinchart
CSE Semaphore Belgium

Chaussee de Bruxelles, 732A
B-1410 Waterloo
Belgium

T +32 (2) 387 42 59
F +32 (2) 387 42 75
#10 lore raw
Quoted 7 lines · Laurent Pinchart
Quoted 6 lines

I believe that he is referring to something much simpler than you are thinking. He is talking about 2-wire RS485 transmissions in which the RTS signal is used to enable the driver chips just before transmitting data and disabling (tri-stating) them just after transmitting data. There are some UARTs that have this behavior built into them.

Quoted 7 lines

And that feature is currently not supported by Linux, even with UARTs that have hardware support for "Auto RS485" mode, such as the 8250-compatible XR16C2850.

The issue is that no flag is defined for the c_cflag field of the termios structure to enable that feature. The following patch is an example of what would be required.

I'd definitely be in favor of such a flag. It sucks having to resort to various hacks to control support of that feature in Linux serial drivers.

Signature · 3 lines
Grant Edwards                   grante             Yow! In Newark the
                                  at               laundromats are open 24
                               visi.com            hours a day!
#12 lore raw
In reply to Grant Edwards
Quoted 3 lines

-----Original Message-----
From: Grant Edwards
Sent: Thursday, July 10, 2008 4:48 PM

...

Quoted 2 lines · Alan Cox

...

Quoted 6 lines
Quoted 1 line

Do we really benefit from having this in kernel ?

People who do industrial communications sure would. I maintain a couple serial drivers that support half-duplex mode exactly as described by the OP, and we have to control that feature via non-standard hacks.

...

I fully agree with Grant Edwards. I, as well, have been myself maintaining two serial drivers that support half-duplex mode exactly as described, and I have to control that feature via non-standard hacks !!!
I have been porting them over and over again with each Linux version and subversion, since Linux 2.2, and there IS a demand for it.

About the benefit to have this in the kernel: well, we would be enabled to implement it for any UART, not just the 16550 series !

Now, about Linux RT and response times: The TXD-to-RTS delay must be as short as possible. To do this, several UARTS i.e. (Ox950) have a special mode. How could you do this in a user land program for any baud rate ? I speak about baud rates up to 2 Mbps, they are in use nowadays on RS2422/RS485 lines.

Really, I would like AT LEAST some commonly defined way to activate this behaviour. Russell King once suggested to create a line discipline to handle the RTS, but even like this we need a way (an ioctl) to activate the behaviour in the UART driver, to make it independent from the specific UART.

JP Tosoni
Acksys