Date: Tue, 11 Mar 2014 11:58:42 +1100 (EST) From: Bruce Evans <brde@optusnet.com.au> To: Marcel Moolenaar <marcel@freebsd.org> Cc: svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org Subject: Re: svn commit: r262955 - head/etc Message-ID: <20140311093634.D1147@besplex.bde.org> In-Reply-To: <201403092051.s29KpEkS051035@svn.freebsd.org> References: <201403092051.s29KpEkS051035@svn.freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
On Sun, 9 Mar 2014, Marcel Moolenaar wrote: > Log: > Add 3wire and std as terminal types/classes. These are similar to > the existing terminal types/classes that have the baudrate suffix, > but differ in that no baudrate is set/defined. > > The purpose of these new types/classes is to allow them to be used > for the serial console. Currently the uart(4) driver fixates the > baudrate and the CLOCAL flag, which means that it doesn't matter > whether you give it std.<baud> or 3wire.<baud> as the terminal type > to getty and what exactly <baud> is set to. It's being overridden > by uart(4). The goal is to change uart(4) not to override these > settings. This was handled correctly (not like the above) in sio starting in about 1994. I refined the Linux locking of tty parameters into the lock-state and initial-state tty devices. Consoles default to locking the speeds, CLOCAL (on) and HUPCL (off), while non-consoles default to not locking anything. Users with write permission on the lock state devices can change either. Fixating the settings in userland requires hacking on /etc/rc.d/serial or somewhere else in /etc/rc or later instead of in /etc/ttys. The initialization is a bit easier and the fixation then applies to all applications that either don't know about the lock state devices or don't have write permission on them. For example, the horrible cu application has a hard-coded default speed on 9600 bps. It blows away the default (initial-state) speed and uses this. To avoid typing in the speed or setting up config files for cu, you can fixate the speed to a more usable value. The version of cu in uucp wasn't as bad. It used the current speed (normally the initial speed), so it worked right without the fixation hack. But it normally needs programming of the initial-state devices in rc or later, to change from the system default speed of 9600 to something more useful. uart breaks this for consoles by ignoring the lock state setting for these parameters and forcing fixed settings instead. Upper layers also break this for CLOCAL. One version ignored the initial state for CLOCAL for callout devices and consoles and forced it on (so any locking of it kept it on). The current version seems to be relatively correct. It doesn't frob CLOCAL at open time, so the effects of this don't persist. However, it seems to have gone backwards for consoles. The TTY_CALLOUT() macro now classifies consoles as non-callout devices. This seems to result in the initial- state and lock-state parameters for the callout devices never being used for consoles. Instead, the initial-state and lock-state parameters for the callin devices are used for consoles. And without the hack to frob CLOCAL on for consoles, CLOCAL in the parameters is needed to avoid blocking in open for consoles. For consoles, it defaults to on for both devices, so the default case works right. CLOCAL is initialized specially for consoles in tty_init_console(). The rest of the function is not so good. It takes a speed parameter, though only one. Most drivers, including uart, neglect to use this parameter. uart fixates instead. tty_init_console() otherwise uses default parameters for the initial-state devices. These are correct for consoles but wrong for non-consoles. It is missing any initialization of the lock-state devices. So these have nothing locked, unless the driver initializes them. uart doesn't initialize them, but uses hard coded fixations to achieve the same result except that applications can't see the fixations that apply by reading the device and can't change the fixations by writing the device. uart also fixates HUPCL to off for consoles. Here are old fixes for this: % diff -c2 ./dev/uart/uart_tty.c~ ./dev/uart/uart_tty.c % *** ./dev/uart/uart_tty.c~ Sat Dec 24 05:22:06 2011 % --- ./dev/uart/uart_tty.c Sat Dec 24 05:22:08 2011 % *************** % *** 241,250 **** % if (t->c_ispeed != t->c_ospeed && t->c_ospeed != 0) % return (EINVAL); % - /* Fixate certain parameters for system devices. */ % - if (sc->sc_sysdev != NULL) { % - t->c_ispeed = t->c_ospeed = sc->sc_sysdev->baudrate; % - t->c_cflag |= CLOCAL; % - t->c_cflag &= ~HUPCL; % - } % if (t->c_ospeed == 0) { % UART_SETSIG(sc, SER_DDTR | SER_DRTS); % --- 241,244 ---- % *************** % *** 383,387 **** % sprintf(((struct consdev *)sc->sc_sysdev->cookie)->cn_name, % "ttyu%r", unit); % ! tty_init_console(tp, 0); % } % % --- 377,381 ---- % sprintf(((struct consdev *)sc->sc_sysdev->cookie)->cn_name, % "ttyu%r", unit); % ! tty_init_console(tp, sc->sc_sysdev->baudrate); % } % For uart, just delete the fixation and use the standard mechanism. This depends on fixing upper layers to initialize CLOCAL and HUPCL in the lock- state devices and ~HUPCL in the initial-state-devices. The upper layers already handled the speed, but uart didn't tell them the speed. The complete patch is much larger, to fix the last bug in many drivers. Other drivers are mostly just missing fixation. % diff -c2 ./kern/tty.c~ ./kern/tty.c % *** ./kern/tty.c~ Fri Mar 2 08:50:08 2012 % --- ./kern/tty.c Fri Mar 2 08:50:09 2012 % *************** % *** 822,829 **** % struct termios *t = &tp->t_termios_init_in; % % t->c_cflag = TTYDEF_CFLAG; % ! t->c_iflag = TTYDEF_IFLAG; % ! t->c_lflag = TTYDEF_LFLAG; % ! t->c_oflag = TTYDEF_OFLAG; % t->c_ispeed = TTYDEF_SPEED; % t->c_ospeed = TTYDEF_SPEED; % --- 852,867 ---- % struct termios *t = &tp->t_termios_init_in; % % + /* % + * Default to raw mode. The defaults in <sys/ttydefaults.h> are % + * really getty's defaults for login terminals, so they must not % + * all be used here. It is most important to have echo flags off % + * initially to prevent echo wars before the echo flags can be % + * turned off. % + */ % t->c_cflag = TTYDEF_CFLAG; % ! t->c_iflag = IGNBRK; % ! t->c_lflag = 0; % ! t->c_oflag = 0; % ! % t->c_ispeed = TTYDEF_SPEED; % t->c_ospeed = TTYDEF_SPEED; This recovers from regressions in the non-console case. % *************** % *** 833,849 **** % } % % void % ! tty_init_console(struct tty *tp, speed_t s) % { % ! struct termios *ti = &tp->t_termios_init_in; % ! struct termios *to = &tp->t_termios_init_out; % % ! if (s != 0) { % ! ti->c_ispeed = ti->c_ospeed = s; % ! to->c_ispeed = to->c_ospeed = s; % ! } % % ! ti->c_cflag |= CLOCAL; % ! to->c_cflag |= CLOCAL; % } % % --- 871,920 ---- % } % % + /* % + * There is no getty for the single-user shell, so we must do its job and % + * change the default flags to ones suitable for logins. Also, if requested % + * to, then change the speed to whatever it actually is and lock this, and % + * unconditionally set CLOCAL and lock this, and unconditionally clear % + * HUPCL and lock this. % + * % + * Callers must tell us their speed and have us lock it iff they have a % + * speed that matters. % + * % + * These settings of CLOCAL and HUPCL are normally backwards for logins, % + * but CLOCAL is enforced so that the console cannot block endlessly when % + * a naive application like syslogd(8) attempts to write to it using a % + * simple open/write/close sequence, and ~HUPCL is enforced to avoid hanging % + * up on close in such a sequence (when the console is not held open by % + * another thread). Output is normally discarded instead of blocking. % + * Most ad hoc writes to the console use wall(8)'s ttymsg() and that is % + * remarkably deficent in terminal handling (it has none, but depends on % + * O_NONBLOCK). Not hanging up is a smaller feature. It matter mainly % + * when the other side of the connection is naive and is not using CLOCAL % + * so the connection gets broken by our hangup. However, if the other side % + * is sophisicated then it might prefer to get signaled on all transitions % + * of carrier. This can be supported by changing the initial state device % + * after booting. We should be more careful about raising and lowering % + * carrier before we are ready and finished, respectively, so that handshakes % + * based on carrier work. % + */ % void % ! tty_init_console(struct tty *tp, speed_t speed) % { % ! struct termios *tinit, *tlock; % % ! tinit = &tp->t_termios_init_in; % ! tlock= &tp->t_termios_lock_in; % ! if (speed != 0) { % ! tinit->c_ispeed = tinit->c_ospeed = speed; % ! tlock->c_ispeed = tlock->c_ospeed = speed; % ! } % ! tinit->c_cflag = (TTYDEF_CFLAG | CLOCAL) & ~HUPCL; % ! tlock->c_cflag = CLOCAL | HUPCL; % ! tinit->c_iflag = TTYDEF_IFLAG; % ! tinit->c_lflag = TTYDEF_LFLAG; % ! tinit->c_oflag = TTYDEF_OFLAG; % % ! tp->t_termios_init_out = *tinit; % ! tp->t_termios_lock_out = *tlock; % } % @@@ Callin and callout devices are almost completely broken. Their interlocking is completely broken. Their only use now is to provide storage for separate termios states (mainly just different CLOCAL settings), so that if you don't care about carrier then you can access the device simplistically using non-blocking opens. For example, "cat /dev/ttyu0" vs "cat /dev/ttyd0". I use such simplistic accesses combined with suitable initializations of the initial-state devices a lot for testing. cat knows nothing of termios or non-blocking opens, so it will tend to block in open on the callin device, but you might not want that. A termios-aware application can start with a nonblocking open of ttyd0 and turn CLOCAL off to get much the same effect as starting with a a blocking open of ttyu0. It should turn off CLOCAL for the latter too, but normally CLOCAL is already off. The separate states allow CLOCAL an other parameters to differ. Linux never had real callin and callout devices. In ~1992, it had something like FreeBSD has know (less complications broken support for real callin and callout devices). IIRC, the "callout" ones were removed 15-20 years ago. Real callin and callout devices have the following primary functionality: you can have a callin device sleeping waiting for carrier in open() (usually by getty()). To use the physical device as a callout device, even in 1980's unixes, you could open it with O_NONBLOCK and set CLOCAL, but setting CLOCAL would have significant effects on the callin application. At best its open would return, and you would have to put the application to sleep somehow. 1980's unixes used fragile userland code for this. It needs lockfiles, and perhaps a combination of select() and polling to wait for the callin application to go away. The races waking up when the callin application goes away are quite complicated, at least without special kernel support. Real callin and callin devices simplify this by duplicating the device state and moving the locking into the kernel. Just duplicating the device state makes things simpler. You can set CLOCAL on the callout device without the callout device seeing it. However, activity on the callout device make cause carrier to rise, and then you still have the complication of putting the callout application to sleep after it sees carrier. This is very easy to handle in the kernel -- you just make the callin application sleep waiting for the callout application to finish instead of waiting for carrier. The details are not quite so simple, and are completely broken in -current. Problems occur even without callout applications. When you have a getty sleeping waiting for carrier, it is useful to be able to examine the device state using stty. "stty </dev/ttyd0" is too simplistic for this, since the open is done by the shell and the shell doesn't understand ttys or nonblocking opens. However, stty has the -f flag to allow this to work: "stty -f /dev/ttyd0" opens with O_NONBLOCK. However, the complete breakage starts with this nonblocking open blocking without bound. Brokenness for callout devices starts in the same way. Opening of ttyu0 blocks on TF_OPENCLOSE for both ttyd0 and ttyu0 before even getting to the check of O_NONBLOCK for ttyd0 or the bypass of this check for ttyu0. So to get working callout devices from applications, you have to work even harder than 1980's unix. You first have to kick off all the gettys, so that your nonblocking open doesn't block. Bruce
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20140311093634.D1147>