Bug 155752 - [uart] tcdrain(3) does not work with uart(4) driver
Summary: [uart] tcdrain(3) does not work with uart(4) driver
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 9.0-CURRENT
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-03-21 15:40 UTC by Poul-Henning Kamp
Modified: 2018-05-20 23:53 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Poul-Henning Kamp 2011-03-21 15:40:01 UTC
	With the change from sio(4) to uart(4), tcdrain(3) broke.

	Now tcdrain(3) only tests if the characters have been
	delivered to the uart(4) subdriver, it does not wait for
	them to have actually been sent on the wire.

	This prevents half-duplex communications with CTS/RTS from
	working.

	This contradicts both our own manpage and The Open Group
	Standards.

Fix: 

There don't seem to be a newbus uart(4) method to ask or
	wait until all transmitted characters have been sent.

	One is obviously necessary.
How-To-Repeat: 
	#define AZ(x) assert ((x) == 0)
	int j, fd;

	fd = open(),
 	makeraw() etc.
	set speed 300BPS

	j = TIOCM_RTS;
        AZ(ioctl(fd, TIOCMBIS, &j));
        assert(3 == write(fd, "FOO", 3))
        AZ(tcdrain(fd));
        AZ(ioctl(fd, TIOCMBIC, &j));

	Observe that RTS flips high + low, long before the three
	characters make it out through the TXD pin.
Comment 1 nkoch 2015-03-04 12:47:44 UTC
I came across the same problem: I need to switch the transmitter
of an external RS232/RS485 converter on before satarting transmittision
and off afterwards using RTS.

My quick driver hack is to always call the chip-specific drain
function whenever there is a request to turn off RTS.

This is my patch to the 16550 code:

--- uart_dev_ns8250.c.orig	2015-03-04 13:37:04.000000000 +0100
+++ uart_dev_ns8250.c	2015-03-04 13:40:05.000000000 +0100
@@ -886,6 +886,10 @@
 		}
 	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
 	uart_lock(sc->sc_hwmtx);
+#ifdef UART_HACK_DRAIN_RTSOFF
+        if ((sig & SER_DRTS) && !(sig & SER_RTS))
+                ns8250_drain(bas, UART_DRAIN_TRANSMITTER);
+#endif
 	ns8250->mcr &= ~(MCR_DTR|MCR_RTS);
 	if (new & SER_DTR)
 		ns8250->mcr |= MCR_DTR;

The patch is against head but has been tested with 9.1
using a scope with protocol analyzer function.

I did not look into the other chips' code so may it
will not universally work.
Comment 2 Eitan Adler freebsd_committer freebsd_triage 2018-05-20 23:53:37 UTC
For bugs matching the following conditions:
- Status == In Progress
- Assignee == "bugs@FreeBSD.org"
- Last Modified Year <= 2017

Do
- Set Status to "Open"