1139804Simp/*-
24Srgrimes * Copyright (c) 1988 University of Utah.
34Srgrimes * Copyright (c) 1991 The Regents of the University of California.
4228643Savg * Copyright (c) 1999 Michael Smith
5228643Savg * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
6228643Savg *
74Srgrimes * All rights reserved.
84Srgrimes *
94Srgrimes * This code is derived from software contributed to Berkeley by
104Srgrimes * the Systems Programming Group of the University of Utah Computer
114Srgrimes * Science Department.
124Srgrimes *
134Srgrimes * Redistribution and use in source and binary forms, with or without
144Srgrimes * modification, are permitted provided that the following conditions
154Srgrimes * are met:
164Srgrimes * 1. Redistributions of source code must retain the above copyright
174Srgrimes *    notice, this list of conditions and the following disclaimer.
184Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
194Srgrimes *    notice, this list of conditions and the following disclaimer in the
204Srgrimes *    documentation and/or other materials provided with the distribution.
214Srgrimes * 4. Neither the name of the University nor the names of its contributors
224Srgrimes *    may be used to endorse or promote products derived from this software
234Srgrimes *    without specific prior written permission.
244Srgrimes *
254Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
264Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
274Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
284Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
294Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
304Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
314Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
324Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
334Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
344Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
354Srgrimes * SUCH DAMAGE.
364Srgrimes *
37620Srgrimes *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
384Srgrimes */
394Srgrimes
40116182Sobrien#include <sys/cdefs.h>
41116182Sobrien__FBSDID("$FreeBSD: stable/11/sys/kern/kern_cons.c 335659 2018-06-26 09:04:24Z avg $");
42116182Sobrien
4387649Sguido#include "opt_ddb.h"
44267965Semaste#include "opt_syscons.h"
4587649Sguido
462056Swollman#include <sys/param.h>
471549Srgrimes#include <sys/systm.h>
48163858Sjb#include <sys/lock.h>
49163858Sjb#include <sys/mutex.h>
505764Sbde#include <sys/conf.h>
5156525Sbde#include <sys/cons.h>
5285448Sjlemon#include <sys/fcntl.h>
53131931Smarcel#include <sys/kdb.h>
5412675Sjulian#include <sys/kernel.h>
5585373Sjlemon#include <sys/malloc.h>
56116663Siedowse#include <sys/msgbuf.h>
5785373Sjlemon#include <sys/namei.h>
58164033Srwatson#include <sys/priv.h>
5969929Sobrien#include <sys/proc.h>
6085373Sjlemon#include <sys/queue.h>
6118951Sjulian#include <sys/reboot.h>
6212701Sphk#include <sys/sysctl.h>
63174905Swkoszek#include <sys/sbuf.h>
642056Swollman#include <sys/tty.h>
6534924Sbde#include <sys/uio.h>
6685373Sjlemon#include <sys/vnode.h>
674Srgrimes
6887620Sguido#include <ddb/ddb.h>
6987620Sguido
7012701Sphk#include <machine/cpu.h>
71177642Sphk#include <machine/clock.h>
724Srgrimes
73179246Sedstatic MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
74179246Sed
7585373Sjlemonstruct cn_device {
7685373Sjlemon	STAILQ_ENTRY(cn_device) cnd_next;
7785373Sjlemon	struct		consdev *cnd_cn;
7885373Sjlemon};
7985373Sjlemon
8085373Sjlemon#define CNDEVPATHMAX	32
8185373Sjlemon#define CNDEVTAB_SIZE	4
8285373Sjlemonstatic struct cn_device cn_devtab[CNDEVTAB_SIZE];
8385373Sjlemonstatic STAILQ_HEAD(, cn_device) cn_devlist =
8485373Sjlemon    STAILQ_HEAD_INITIALIZER(cn_devlist);
8585373Sjlemon
86125467Skanint	cons_avail_mask = 0;	/* Bit mask. Each registered low level console
87125467Skan				 * which is currently unavailable for inpit
88125467Skan				 * (i.e., if it is in graphics mode) will have
89125467Skan				 * this bit cleared.
907680Sjoerg				 */
9185373Sjlemonstatic int cn_mute;
92116663Siedowsestatic char *consbuf;			/* buffer used by `consmsgbuf' */
93116663Siedowsestatic struct callout conscallout;	/* callout for outputting to constty */
94116663Siedowsestruct msgbuf consmsgbuf;		/* message buffer for console tty */
9587620Sguidostatic u_char console_pausing;		/* pause after each line during probe */
9687620Sguidostatic char *console_pausestr=
9787620Sguido"<pause; press any key to proceed to next line or '.' to end pause mode>";
98116663Siedowsestruct tty *constty;			/* pointer to console "window" tty */
99163858Sjbstatic struct mtx cnputs_mtx;		/* Mutex for cnputs(). */
100163858Sjbstatic int use_cnputs_mtx = 0;		/* != 0 if cnputs_mtx locking reqd. */
1015764Sbde
102116663Siedowsestatic void constty_timeout(void *arg);
10385448Sjlemon
104158944Sphkstatic struct consdev cons_consdev;
105158944SphkDATA_SET(cons_set, cons_consdev);
10678161SpeterSET_DECLARE(cons_set, struct consdev);
10742373Syokota
108798Swollmanvoid
10985373Sjlemoncninit(void)
1104Srgrimes{
11185373Sjlemon	struct consdev *best_cn, *cn, **list;
1124Srgrimes
1134Srgrimes	/*
11418951Sjulian	 * Check if we should mute the console (for security reasons perhaps)
11518951Sjulian	 * It can be changes dynamically using sysctl kern.consmute
11618951Sjulian	 * once we are up and going.
11718951Sjulian	 *
11818951Sjulian	 */
11918951Sjulian        cn_mute = ((boothowto & (RB_MUTE
12018951Sjulian			|RB_SINGLE
12118951Sjulian			|RB_VERBOSE
122138249Sscottl			|RB_ASKNAME)) == RB_MUTE);
12385373Sjlemon
12418951Sjulian	/*
12585373Sjlemon	 * Find the first console with the highest priority.
1264Srgrimes	 */
12785373Sjlemon	best_cn = NULL;
12885373Sjlemon	SET_FOREACH(list, cons_set) {
12985373Sjlemon		cn = *list;
130101436Sjake		cnremove(cn);
131196506Sed		/* Skip cons_consdev. */
132196506Sed		if (cn->cn_ops == NULL)
13385373Sjlemon			continue;
134196506Sed		cn->cn_ops->cn_probe(cn);
13585373Sjlemon		if (cn->cn_pri == CN_DEAD)
13685373Sjlemon			continue;
13785373Sjlemon		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
13885373Sjlemon			best_cn = cn;
13985373Sjlemon		if (boothowto & RB_MULTIPLE) {
14085373Sjlemon			/*
14185373Sjlemon			 * Initialize console, and attach to it.
14285373Sjlemon			 */
143196506Sed			cn->cn_ops->cn_init(cn);
14485373Sjlemon			cnadd(cn);
14585373Sjlemon		}
14685373Sjlemon	}
14785373Sjlemon	if (best_cn == NULL)
1484Srgrimes		return;
14985373Sjlemon	if ((boothowto & RB_MULTIPLE) == 0) {
150196506Sed		best_cn->cn_ops->cn_init(best_cn);
15185373Sjlemon		cnadd(best_cn);
15210665Sbde	}
15387620Sguido	if (boothowto & RB_PAUSE)
15487620Sguido		console_pausing = 1;
1554Srgrimes	/*
15685373Sjlemon	 * Make the best console the preferred console.
15710665Sbde	 */
15885373Sjlemon	cnselect(best_cn);
159274711Szbb
160274711Szbb#ifdef EARLY_PRINTF
161274711Szbb	/*
162274711Szbb	 * Release early console.
163274711Szbb	 */
164274711Szbb	early_putc = NULL;
165274711Szbb#endif
16685373Sjlemon}
16785373Sjlemon
16887620Sguidovoid
16987620Sguidocninit_finish()
17087620Sguido{
17187620Sguido	console_pausing = 0;
17287620Sguido}
17387620Sguido
17485373Sjlemon/* add a new physical console to back the virtual console */
17585373Sjlemonint
17685373Sjlemoncnadd(struct consdev *cn)
17785373Sjlemon{
17885373Sjlemon	struct cn_device *cnd;
17985373Sjlemon	int i;
18085373Sjlemon
18185373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
18285373Sjlemon		if (cnd->cnd_cn == cn)
18385373Sjlemon			return (0);
18485373Sjlemon	for (i = 0; i < CNDEVTAB_SIZE; i++) {
18585373Sjlemon		cnd = &cn_devtab[i];
18685373Sjlemon		if (cnd->cnd_cn == NULL)
18785373Sjlemon			break;
18848104Syokota	}
18985373Sjlemon	if (cnd->cnd_cn != NULL)
19085373Sjlemon		return (ENOMEM);
19185373Sjlemon	cnd->cnd_cn = cn;
192120456Sphk	if (cn->cn_name[0] == '\0') {
193120456Sphk		/* XXX: it is unclear if/where this print might output */
194120456Sphk		printf("WARNING: console at %p has no name\n", cn);
195120456Sphk	}
19685373Sjlemon	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
197184521Sed	if (STAILQ_FIRST(&cn_devlist) == cnd)
198184521Sed		ttyconsdev_select(cnd->cnd_cn->cn_name);
199125467Skan
200125467Skan	/* Add device to the active mask. */
201125467Skan	cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
202125467Skan
20385373Sjlemon	return (0);
20410665Sbde}
20510665Sbde
20610665Sbdevoid
20785373Sjlemoncnremove(struct consdev *cn)
20810665Sbde{
20985373Sjlemon	struct cn_device *cnd;
210125467Skan	int i;
21110665Sbde
21285373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
21385373Sjlemon		if (cnd->cnd_cn != cn)
21485373Sjlemon			continue;
215184521Sed		if (STAILQ_FIRST(&cn_devlist) == cnd)
216184521Sed			ttyconsdev_select(NULL);
21785373Sjlemon		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
21885373Sjlemon		cnd->cnd_cn = NULL;
219125467Skan
220125467Skan		/* Remove this device from available mask. */
221125467Skan		for (i = 0; i < CNDEVTAB_SIZE; i++)
222125467Skan			if (cnd == &cn_devtab[i]) {
223125467Skan				cons_avail_mask &= ~(1 << i);
224125467Skan				break;
225125467Skan			}
22685373Sjlemon#if 0
22785373Sjlemon		/*
22885373Sjlemon		 * XXX
22985373Sjlemon		 * syscons gets really confused if console resources are
23085373Sjlemon		 * freed after the system has initialized.
23185373Sjlemon		 */
23285373Sjlemon		if (cn->cn_term != NULL)
233196506Sed			cn->cn_ops->cn_term(cn);
23485373Sjlemon#endif
23510665Sbde		return;
23656582Sbde	}
2374Srgrimes}
2384Srgrimes
23985373Sjlemonvoid
24085373Sjlemoncnselect(struct consdev *cn)
24127982Sjulian{
24285373Sjlemon	struct cn_device *cnd;
24327982Sjulian
24485373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
24585373Sjlemon		if (cnd->cnd_cn != cn)
24685373Sjlemon			continue;
24785373Sjlemon		if (cnd == STAILQ_FIRST(&cn_devlist))
24885373Sjlemon			return;
24985373Sjlemon		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
25085373Sjlemon		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
251184521Sed		ttyconsdev_select(cnd->cnd_cn->cn_name);
25227982Sjulian		return;
25385373Sjlemon	}
25485373Sjlemon}
25527982Sjulian
25685373Sjlemonvoid
257125467Skancnavailable(struct consdev *cn, int available)
258125467Skan{
259125467Skan	int i;
260125467Skan
261125467Skan	for (i = 0; i < CNDEVTAB_SIZE; i++) {
262125467Skan		if (cn_devtab[i].cnd_cn == cn)
263125467Skan			break;
264125467Skan	}
265125467Skan	if (available) {
266125467Skan		if (i < CNDEVTAB_SIZE)
267125467Skan			cons_avail_mask |= (1 << i);
268125467Skan		cn->cn_flags &= ~CN_FLAG_NOAVAIL;
269125467Skan	} else {
270125467Skan		if (i < CNDEVTAB_SIZE)
271125467Skan			cons_avail_mask &= ~(1 << i);
272125467Skan		cn->cn_flags |= CN_FLAG_NOAVAIL;
273125467Skan	}
274125467Skan}
275125467Skan
276125467Skanint
277125487Skancnunavailable(void)
278125467Skan{
279125487Skan
280125467Skan	return (cons_avail_mask == 0);
281125467Skan}
282125467Skan
283120456Sphk/*
284174905Swkoszek * sysctl_kern_console() provides output parseable in conscontrol(1).
285120456Sphk */
28685373Sjlemonstatic int
28785373Sjlemonsysctl_kern_console(SYSCTL_HANDLER_ARGS)
28885373Sjlemon{
28985373Sjlemon	struct cn_device *cnd;
29085373Sjlemon	struct consdev *cp, **list;
291174905Swkoszek	char *p;
292174905Swkoszek	int delete, error;
293174905Swkoszek	struct sbuf *sb;
29485373Sjlemon
295280015Sian	sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND |
296280015Sian	    SBUF_INCLUDENUL);
297174905Swkoszek	if (sb == NULL)
298174905Swkoszek		return (ENOMEM);
299174905Swkoszek	sbuf_clear(sb);
30085373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
301174905Swkoszek		sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name);
302174905Swkoszek	sbuf_printf(sb, "/");
30385373Sjlemon	SET_FOREACH(list, cons_set) {
30485373Sjlemon		cp = *list;
305120456Sphk		if (cp->cn_name[0] != '\0')
306174905Swkoszek			sbuf_printf(sb, "%s,", cp->cn_name);
30785373Sjlemon	}
308174905Swkoszek	sbuf_finish(sb);
309174905Swkoszek	error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
31085373Sjlemon	if (error == 0 && req->newptr != NULL) {
311174905Swkoszek		p = sbuf_data(sb);
31285373Sjlemon		error = ENXIO;
31385373Sjlemon		delete = 0;
31485373Sjlemon		if (*p == '-') {
31585373Sjlemon			delete = 1;
31685373Sjlemon			p++;
31785373Sjlemon		}
31885373Sjlemon		SET_FOREACH(list, cons_set) {
31985373Sjlemon			cp = *list;
320120456Sphk			if (strcmp(p, cp->cn_name) != 0)
32185373Sjlemon				continue;
32285373Sjlemon			if (delete) {
32385373Sjlemon				cnremove(cp);
32485373Sjlemon				error = 0;
32585373Sjlemon			} else {
32685373Sjlemon				error = cnadd(cp);
32785373Sjlemon				if (error == 0)
32885373Sjlemon					cnselect(cp);
32985373Sjlemon			}
33085373Sjlemon			break;
33185373Sjlemon		}
33285373Sjlemon	}
333174905Swkoszek	sbuf_delete(sb);
33485373Sjlemon	return (error);
33527982Sjulian}
33627982Sjulian
33785373SjlemonSYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
33885373Sjlemon	0, 0, sysctl_kern_console, "A", "Console device control");
33985373Sjlemon
34027982Sjulian/*
34127982Sjulian * User has changed the state of the console muting.
34227982Sjulian * This may require us to open or close the device in question.
34327982Sjulian */
34412675Sjulianstatic int
34562573Sphksysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
34627982Sjulian{
34727982Sjulian	int error;
34827982Sjulian
34927982Sjulian	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
35085373Sjlemon	if (error != 0 || req->newptr == NULL)
35185373Sjlemon		return (error);
35227982Sjulian	return (error);
35327982Sjulian}
35427982Sjulian
35527982SjulianSYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
356211102Sgavin	0, sizeof(cn_mute), sysctl_kern_consmute, "I",
357211102Sgavin	"State of the console muting");
35827982Sjulian
359228631Savgvoid
360228631Savgcngrab()
361228631Savg{
362228631Savg	struct cn_device *cnd;
363228631Savg	struct consdev *cn;
364228631Savg
365228631Savg	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
366228631Savg		cn = cnd->cnd_cn;
367228631Savg		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
368228631Savg			cn->cn_ops->cn_grab(cn);
369228631Savg	}
370228631Savg}
371228631Savg
372228631Savgvoid
373228631Savgcnungrab()
374228631Savg{
375228631Savg	struct cn_device *cnd;
376228631Savg	struct consdev *cn;
377228631Savg
378228631Savg	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
379228631Savg		cn = cnd->cnd_cn;
380228631Savg		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
381228631Savg			cn->cn_ops->cn_ungrab(cn);
382228631Savg	}
383228631Savg}
384228631Savg
385335659Savgvoid
386335659Savgcnresume()
387335659Savg{
388335659Savg	struct cn_device *cnd;
389335659Savg	struct consdev *cn;
390335659Savg
391335659Savg	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
392335659Savg		cn = cnd->cnd_cn;
393335659Savg		if (cn->cn_ops->cn_resume != NULL)
394335659Savg			cn->cn_ops->cn_resume(cn);
395335659Savg	}
396335659Savg}
397335659Savg
39885373Sjlemon/*
39985373Sjlemon * Low level console routines.
40085373Sjlemon */
401798Swollmanint
40285373Sjlemoncngetc(void)
4034Srgrimes{
4045160Sjoerg	int c;
40585373Sjlemon
40685373Sjlemon	if (cn_mute)
40719268Sjulian		return (-1);
40885373Sjlemon	while ((c = cncheckc()) == -1)
409241295Savg		cpu_spinwait();
41085373Sjlemon	if (c == '\r')
41185373Sjlemon		c = '\n';		/* console input is always ICRNL */
4125160Sjoerg	return (c);
4134Srgrimes}
4144Srgrimes
4153728Sphkint
41685373Sjlemoncncheckc(void)
4173728Sphk{
41885373Sjlemon	struct cn_device *cnd;
41985373Sjlemon	struct consdev *cn;
42085373Sjlemon	int c;
42185373Sjlemon
42285373Sjlemon	if (cn_mute)
42318287Sbde		return (-1);
42485373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
42585373Sjlemon		cn = cnd->cnd_cn;
426131931Smarcel		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
427196506Sed			c = cn->cn_ops->cn_getc(cn);
428196506Sed			if (c != -1)
429121182Srwatson				return (c);
43085373Sjlemon		}
43185373Sjlemon	}
43285373Sjlemon	return (-1);
4333728Sphk}
4343728Sphk
435798Swollmanvoid
436228633Savgcngets(char *cp, size_t size, int visible)
437228633Savg{
438228633Savg	char *lp, *end;
439228633Savg	int c;
440228633Savg
441228633Savg	cngrab();
442228633Savg
443228633Savg	lp = cp;
444228633Savg	end = cp + size - 1;
445228633Savg	for (;;) {
446228633Savg		c = cngetc() & 0177;
447228633Savg		switch (c) {
448228633Savg		case '\n':
449228633Savg		case '\r':
450228633Savg			cnputc(c);
451228633Savg			*lp = '\0';
452228633Savg			cnungrab();
453228633Savg			return;
454228633Savg		case '\b':
455228633Savg		case '\177':
456228633Savg			if (lp > cp) {
457260118Simp				if (visible)
458260118Simp					cnputs("\b \b");
459228633Savg				lp--;
460228633Savg			}
461228633Savg			continue;
462228633Savg		case '\0':
463228633Savg			continue;
464228633Savg		default:
465228633Savg			if (lp < end) {
466228633Savg				switch (visible) {
467228633Savg				case GETS_NOECHO:
468228633Savg					break;
469228633Savg				case GETS_ECHOPASS:
470228633Savg					cnputc('*');
471228633Savg					break;
472228633Savg				default:
473228633Savg					cnputc(c);
474228633Savg					break;
475228633Savg				}
476228633Savg				*lp++ = c;
477228633Savg			}
478228633Savg		}
479228633Savg	}
480228633Savg}
481228633Savg
482228633Savgvoid
48385373Sjlemoncnputc(int c)
4844Srgrimes{
48585373Sjlemon	struct cn_device *cnd;
48685373Sjlemon	struct consdev *cn;
48787620Sguido	char *cp;
48885373Sjlemon
489261786Sian#ifdef EARLY_PRINTF
490261786Sian	if (early_putc != NULL) {
491261786Sian		if (c == '\n')
492261786Sian			early_putc('\r');
493261786Sian		early_putc(c);
494261786Sian		return;
495261786Sian	}
496261786Sian#endif
497261786Sian
49885373Sjlemon	if (cn_mute || c == '\0')
4994Srgrimes		return;
50085373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
50185373Sjlemon		cn = cnd->cnd_cn;
502131931Smarcel		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
503121182Srwatson			if (c == '\n')
504196506Sed				cn->cn_ops->cn_putc(cn, '\r');
505196506Sed			cn->cn_ops->cn_putc(cn, c);
506121182Srwatson		}
5074Srgrimes	}
508131931Smarcel	if (console_pausing && c == '\n' && !kdb_active) {
50987620Sguido		for (cp = console_pausestr; *cp != '\0'; cp++)
51087620Sguido			cnputc(*cp);
511228632Savg		cngrab();
51287620Sguido		if (cngetc() == '.')
51387620Sguido			console_pausing = 0;
514228632Savg		cnungrab();
51587620Sguido		cnputc('\r');
51687620Sguido		for (cp = console_pausestr; *cp != '\0'; cp++)
51787620Sguido			cnputc(' ');
51887620Sguido		cnputc('\r');
51987620Sguido	}
5204Srgrimes}
5214Srgrimes
522163858Sjbvoid
523163858Sjbcnputs(char *p)
524163858Sjb{
525163858Sjb	int c;
526163858Sjb	int unlock_reqd = 0;
527163858Sjb
528163858Sjb	if (use_cnputs_mtx) {
529276626Shselasky	  	/*
530276626Shselasky		 * NOTE: Debug prints and/or witness printouts in
531276626Shselasky		 * console driver clients can cause the "cnputs_mtx"
532276626Shselasky		 * mutex to recurse. Simply return if that happens.
533276626Shselasky		 */
534276626Shselasky		if (mtx_owned(&cnputs_mtx))
535276626Shselasky			return;
536163858Sjb		mtx_lock_spin(&cnputs_mtx);
537163858Sjb		unlock_reqd = 1;
538163858Sjb	}
539163858Sjb
540163858Sjb	while ((c = *p++) != '\0')
541163858Sjb		cnputc(c);
542163858Sjb
543163858Sjb	if (unlock_reqd)
544163858Sjb		mtx_unlock_spin(&cnputs_mtx);
545163858Sjb}
546163858Sjb
547116663Siedowsestatic int consmsgbuf_size = 8192;
548116663SiedowseSYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
549211102Sgavin    "Console tty buffer size");
550116663Siedowse
551116663Siedowse/*
552116663Siedowse * Redirect console output to a tty.
553116663Siedowse */
554116663Siedowsevoid
555116663Siedowseconstty_set(struct tty *tp)
556116663Siedowse{
557116663Siedowse	int size;
558116663Siedowse
559116663Siedowse	KASSERT(tp != NULL, ("constty_set: NULL tp"));
560116663Siedowse	if (consbuf == NULL) {
561116663Siedowse		size = consmsgbuf_size;
562179246Sed		consbuf = malloc(size, M_TTYCONS, M_WAITOK);
563116663Siedowse		msgbuf_init(&consmsgbuf, consbuf, size);
564116663Siedowse		callout_init(&conscallout, 0);
565116663Siedowse	}
566116663Siedowse	constty = tp;
567116663Siedowse	constty_timeout(NULL);
568116663Siedowse}
569116663Siedowse
570116663Siedowse/*
571116663Siedowse * Disable console redirection to a tty.
572116663Siedowse */
573116663Siedowsevoid
574116663Siedowseconstty_clear(void)
575116663Siedowse{
576116663Siedowse	int c;
577116663Siedowse
578116663Siedowse	constty = NULL;
579116663Siedowse	if (consbuf == NULL)
580116663Siedowse		return;
581116663Siedowse	callout_stop(&conscallout);
582116663Siedowse	while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
583116663Siedowse		cnputc(c);
584179246Sed	free(consbuf, M_TTYCONS);
585116663Siedowse	consbuf = NULL;
586116663Siedowse}
587116663Siedowse
588116663Siedowse/* Times per second to check for pending console tty messages. */
589116663Siedowsestatic int constty_wakeups_per_second = 5;
590116663SiedowseSYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
591211102Sgavin    &constty_wakeups_per_second, 0,
592211102Sgavin    "Times per second to check for pending console tty messages");
593116663Siedowse
594112046Sphkstatic void
595116663Siedowseconstty_timeout(void *arg)
596116663Siedowse{
597116663Siedowse	int c;
598116663Siedowse
599181905Sed	if (constty != NULL) {
600181905Sed		tty_lock(constty);
601181905Sed		while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
602181905Sed			if (tty_putchar(constty, c) < 0) {
603181905Sed				tty_unlock(constty);
604181905Sed				constty = NULL;
605181905Sed				break;
606181905Sed			}
607181905Sed		}
608181905Sed
609181905Sed		if (constty != NULL)
610181905Sed			tty_unlock(constty);
611116663Siedowse	}
612116663Siedowse	if (constty != NULL) {
613116663Siedowse		callout_reset(&conscallout, hz / constty_wakeups_per_second,
614116663Siedowse		    constty_timeout, NULL);
615116663Siedowse	} else {
616116663Siedowse		/* Deallocate the constty buffer memory. */
617116663Siedowse		constty_clear();
618116663Siedowse	}
619116663Siedowse}
620116663Siedowse
621116663Siedowsestatic void
622112046Sphkcn_drvinit(void *unused)
623112046Sphk{
624112046Sphk
625276626Shselasky	mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
626163858Sjb	use_cnputs_mtx = 1;
627112046Sphk}
628112046Sphk
629177253SrwatsonSYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
630177642Sphk
631177642Sphk/*
632177642Sphk * Sysbeep(), if we have hardware for it
633177642Sphk */
634177642Sphk
635177642Sphk#ifdef HAS_TIMER_SPKR
636177642Sphk
637177642Sphkstatic int beeping;
638271963Sjhbstatic struct callout beeping_timer;
639177642Sphk
640177642Sphkstatic void
641177642Sphksysbeepstop(void *chan)
642177642Sphk{
643177642Sphk
644177642Sphk	timer_spkr_release();
645177642Sphk	beeping = 0;
646177642Sphk}
647177642Sphk
648177642Sphkint
649177642Sphksysbeep(int pitch, int period)
650177642Sphk{
651177642Sphk
652177642Sphk	if (timer_spkr_acquire()) {
653177642Sphk		if (!beeping) {
654177642Sphk			/* Something else owns it. */
655177642Sphk			return (EBUSY);
656177642Sphk		}
657177642Sphk	}
658177642Sphk	timer_spkr_setfreq(pitch);
659177642Sphk	if (!beeping) {
660177642Sphk		beeping = period;
661271963Sjhb		callout_reset(&beeping_timer, period, sysbeepstop, NULL);
662177642Sphk	}
663177642Sphk	return (0);
664177642Sphk}
665177642Sphk
666271963Sjhbstatic void
667271963Sjhbsysbeep_init(void *unused)
668271963Sjhb{
669271963Sjhb
670283291Sjkim	callout_init(&beeping_timer, 1);
671271963Sjhb}
672271963SjhbSYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL);
673177642Sphk#else
674177642Sphk
675177642Sphk/*
676177642Sphk * No hardware, no sound
677177642Sphk */
678177642Sphk
679177642Sphkint
680177642Sphksysbeep(int pitch __unused, int period __unused)
681177642Sphk{
682177642Sphk
683177642Sphk	return (ENODEV);
684177642Sphk}
685177642Sphk
686177642Sphk#endif
687177642Sphk
688267965Semaste/*
689267965Semaste * Temporary support for sc(4) to vt(4) transition.
690267965Semaste */
691268158Semastestatic unsigned vty_prefer;
692267992Shselaskystatic char vty_name[16];
693267992ShselaskySYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, vty_name,
694267992Shselasky    0, "Console vty driver");
695267965Semaste
696267965Semasteint
697267965Semastevty_enabled(unsigned vty)
698267965Semaste{
699267965Semaste	static unsigned vty_selected = 0;
700267965Semaste
701267965Semaste	if (vty_selected == 0) {
702267965Semaste		TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name));
703267965Semaste		do {
704267965Semaste#if defined(DEV_SC)
705267965Semaste			if (strcmp(vty_name, "sc") == 0) {
706267965Semaste				vty_selected = VTY_SC;
707267965Semaste				break;
708267965Semaste			}
709267965Semaste#endif
710267965Semaste#if defined(DEV_VT)
711267965Semaste			if (strcmp(vty_name, "vt") == 0) {
712267965Semaste				vty_selected = VTY_VT;
713267965Semaste				break;
714267965Semaste			}
715267965Semaste#endif
716268158Semaste			if (vty_prefer != 0) {
717268158Semaste				vty_selected = vty_prefer;
718268158Semaste				break;
719268158Semaste			}
720274085Sdumbbell#if defined(DEV_VT)
721274085Sdumbbell			vty_selected = VTY_VT;
722274085Sdumbbell#elif defined(DEV_SC)
723267965Semaste			vty_selected = VTY_SC;
724267965Semaste#endif
725267965Semaste		} while (0);
726267965Semaste
727267965Semaste		if (vty_selected == VTY_VT)
728267965Semaste			strcpy(vty_name, "vt");
729267965Semaste		else if (vty_selected == VTY_SC)
730267965Semaste			strcpy(vty_name, "sc");
731267965Semaste	}
732267965Semaste	return ((vty_selected & vty) != 0);
733267965Semaste}
734267965Semaste
735268158Semastevoid
736268158Semastevty_set_preferred(unsigned vty)
737268158Semaste{
738268158Semaste
739268158Semaste	vty_prefer = vty;
740268158Semaste#if !defined(DEV_SC)
741268160Semaste	vty_prefer &= ~VTY_SC;
742268158Semaste#endif
743268158Semaste#if !defined(DEV_VT)
744268160Semaste	vty_prefer &= ~VTY_VT;
745268158Semaste#endif
746268158Semaste}
747268158Semaste
748