kern_cons.c revision 116663
14Srgrimes/*
24Srgrimes * Copyright (c) 1988 University of Utah.
34Srgrimes * Copyright (c) 1991 The Regents of the University of California.
44Srgrimes * All rights reserved.
54Srgrimes *
64Srgrimes * This code is derived from software contributed to Berkeley by
74Srgrimes * the Systems Programming Group of the University of Utah Computer
84Srgrimes * Science Department.
94Srgrimes *
104Srgrimes * Redistribution and use in source and binary forms, with or without
114Srgrimes * modification, are permitted provided that the following conditions
124Srgrimes * are met:
134Srgrimes * 1. Redistributions of source code must retain the above copyright
144Srgrimes *    notice, this list of conditions and the following disclaimer.
154Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
164Srgrimes *    notice, this list of conditions and the following disclaimer in the
174Srgrimes *    documentation and/or other materials provided with the distribution.
184Srgrimes * 3. All advertising materials mentioning features or use of this software
194Srgrimes *    must display the following acknowledgement:
204Srgrimes *	This product includes software developed by the University of
214Srgrimes *	California, Berkeley and its contributors.
224Srgrimes * 4. Neither the name of the University nor the names of its contributors
234Srgrimes *    may be used to endorse or promote products derived from this software
244Srgrimes *    without specific prior written permission.
254Srgrimes *
264Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
274Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
284Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
294Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
304Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
314Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
324Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
334Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
344Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
354Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
364Srgrimes * SUCH DAMAGE.
374Srgrimes *
38620Srgrimes *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
394Srgrimes */
404Srgrimes
41116182Sobrien#include <sys/cdefs.h>
42116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/tty_cons.c 116663 2003-06-22 02:54:33Z iedowse $");
43116182Sobrien
4487649Sguido#include "opt_ddb.h"
4587649Sguido
462056Swollman#include <sys/param.h>
471549Srgrimes#include <sys/systm.h>
485764Sbde#include <sys/conf.h>
4956525Sbde#include <sys/cons.h>
5085448Sjlemon#include <sys/fcntl.h>
5112675Sjulian#include <sys/kernel.h>
5285373Sjlemon#include <sys/malloc.h>
53116663Siedowse#include <sys/msgbuf.h>
5485373Sjlemon#include <sys/namei.h>
5569929Sobrien#include <sys/proc.h>
5685373Sjlemon#include <sys/queue.h>
5718951Sjulian#include <sys/reboot.h>
5812701Sphk#include <sys/sysctl.h>
592056Swollman#include <sys/tty.h>
6034924Sbde#include <sys/uio.h>
6185373Sjlemon#include <sys/vnode.h>
624Srgrimes
6387620Sguido#include <ddb/ddb.h>
6487620Sguido
6512701Sphk#include <machine/cpu.h>
664Srgrimes
6712675Sjulianstatic	d_open_t	cnopen;
6812675Sjulianstatic	d_close_t	cnclose;
6912675Sjulianstatic	d_read_t	cnread;
7012675Sjulianstatic	d_write_t	cnwrite;
7112675Sjulianstatic	d_ioctl_t	cnioctl;
7229368Speterstatic	d_poll_t	cnpoll;
7372521Sjlemonstatic	d_kqfilter_t	cnkqfilter;
7412675Sjulian
7547625Sphkstatic struct cdevsw cn_cdevsw = {
76111815Sphk	.d_open =	cnopen,
77111815Sphk	.d_close =	cnclose,
78111815Sphk	.d_read =	cnread,
79111815Sphk	.d_write =	cnwrite,
80111815Sphk	.d_ioctl =	cnioctl,
81111815Sphk	.d_poll =	cnpoll,
82111815Sphk	.d_name =	"console",
83112035Sphk	.d_maj =	256,
84112035Sphk			/*
85112035Sphk			 * XXX: We really want major #0, but zero here means
86112035Sphk			 * XXX: allocate a major number automatically.
87112035Sphk			 * XXX: kern_conf.c knows what to do when it sees 256.
88112035Sphk			 */
89111821Sphk	.d_flags =	D_TTY,
90111815Sphk	.d_kqfilter =	cnkqfilter,
9138485Sbde};
9212675Sjulian
9385373Sjlemonstruct cn_device {
9485373Sjlemon	STAILQ_ENTRY(cn_device) cnd_next;
9585373Sjlemon	char		cnd_name[16];
9685373Sjlemon	struct		vnode *cnd_vp;
9785373Sjlemon	struct		consdev *cnd_cn;
9885373Sjlemon};
9985373Sjlemon
10085373Sjlemon#define CNDEVPATHMAX	32
10185373Sjlemon#define CNDEVTAB_SIZE	4
10285373Sjlemonstatic struct cn_device cn_devtab[CNDEVTAB_SIZE];
10385373Sjlemonstatic STAILQ_HEAD(, cn_device) cn_devlist =
10485373Sjlemon    STAILQ_HEAD_INITIALIZER(cn_devlist);
10585373Sjlemon
10685373Sjlemon#define CND_INVALID(cnd, td) 						\
10785373Sjlemon	(cnd == NULL || cnd->cnd_vp == NULL ||				\
10885373Sjlemon	    (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
10985373Sjlemon
11049049Syokotastatic udev_t	cn_udev_t;
11141612SeivindSYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
11249049Syokota	&cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
11327982Sjulian
1147680Sjoergint	cons_unavail = 0;	/* XXX:
1157680Sjoerg				 * physical console not available for
1167680Sjoerg				 * input (i.e., it is in graphics mode)
1177680Sjoerg				 */
11885373Sjlemonstatic int cn_mute;
11985373Sjlemonstatic int openflag;			/* how /dev/console was opened */
12085373Sjlemonstatic int cn_is_open;
121116663Siedowsestatic char *consbuf;			/* buffer used by `consmsgbuf' */
122116663Siedowsestatic struct callout conscallout;	/* callout for outputting to constty */
123116663Siedowsestruct msgbuf consmsgbuf;		/* message buffer for console tty */
12487620Sguidostatic u_char console_pausing;		/* pause after each line during probe */
12587620Sguidostatic char *console_pausestr=
12687620Sguido"<pause; press any key to proceed to next line or '.' to end pause mode>";
127116663Siedowsestruct tty *constty;			/* pointer to console "window" tty */
1285764Sbde
12985448Sjlemonvoid	cndebug(char *);
130116663Siedowsestatic void constty_timeout(void *arg);
13185448Sjlemon
13255823SyokotaCONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
13378161SpeterSET_DECLARE(cons_set, struct consdev);
13442373Syokota
135798Swollmanvoid
13685373Sjlemoncninit(void)
1374Srgrimes{
13885373Sjlemon	struct consdev *best_cn, *cn, **list;
1394Srgrimes
1404Srgrimes	/*
14118951Sjulian	 * Check if we should mute the console (for security reasons perhaps)
14218951Sjulian	 * It can be changes dynamically using sysctl kern.consmute
14318951Sjulian	 * once we are up and going.
14418951Sjulian	 *
14518951Sjulian	 */
14618951Sjulian        cn_mute = ((boothowto & (RB_MUTE
14718951Sjulian			|RB_SINGLE
14818951Sjulian			|RB_VERBOSE
14918951Sjulian			|RB_ASKNAME
15018951Sjulian			|RB_CONFIG)) == RB_MUTE);
15185373Sjlemon
15218951Sjulian	/*
15385373Sjlemon	 * Find the first console with the highest priority.
1544Srgrimes	 */
15585373Sjlemon	best_cn = NULL;
15685373Sjlemon	SET_FOREACH(list, cons_set) {
15785373Sjlemon		cn = *list;
158101436Sjake		cnremove(cn);
15985373Sjlemon		if (cn->cn_probe == NULL)
16085373Sjlemon			continue;
16185373Sjlemon		cn->cn_probe(cn);
16285373Sjlemon		if (cn->cn_pri == CN_DEAD)
16385373Sjlemon			continue;
16485373Sjlemon		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
16585373Sjlemon			best_cn = cn;
16685373Sjlemon		if (boothowto & RB_MULTIPLE) {
16785373Sjlemon			/*
16885373Sjlemon			 * Initialize console, and attach to it.
16985373Sjlemon			 */
17085373Sjlemon			cnadd(cn);
17185373Sjlemon			cn->cn_init(cn);
17285373Sjlemon		}
17385373Sjlemon	}
17485373Sjlemon	if (best_cn == NULL)
1754Srgrimes		return;
17685373Sjlemon	if ((boothowto & RB_MULTIPLE) == 0) {
17785373Sjlemon		cnadd(best_cn);
17885373Sjlemon		best_cn->cn_init(best_cn);
17910665Sbde	}
18087620Sguido	if (boothowto & RB_PAUSE)
18187620Sguido		console_pausing = 1;
1824Srgrimes	/*
18385373Sjlemon	 * Make the best console the preferred console.
18410665Sbde	 */
18585373Sjlemon	cnselect(best_cn);
18685373Sjlemon}
18785373Sjlemon
18887620Sguidovoid
18987620Sguidocninit_finish()
19087620Sguido{
19187620Sguido	console_pausing = 0;
19287620Sguido}
19387620Sguido
19485373Sjlemon/* add a new physical console to back the virtual console */
19585373Sjlemonint
19685373Sjlemoncnadd(struct consdev *cn)
19785373Sjlemon{
19885373Sjlemon	struct cn_device *cnd;
19985373Sjlemon	int i;
20085373Sjlemon
20185373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
20285373Sjlemon		if (cnd->cnd_cn == cn)
20385373Sjlemon			return (0);
20485373Sjlemon	for (i = 0; i < CNDEVTAB_SIZE; i++) {
20585373Sjlemon		cnd = &cn_devtab[i];
20685373Sjlemon		if (cnd->cnd_cn == NULL)
20785373Sjlemon			break;
20848104Syokota	}
20985373Sjlemon	if (cnd->cnd_cn != NULL)
21085373Sjlemon		return (ENOMEM);
21185373Sjlemon	cnd->cnd_cn = cn;
21285373Sjlemon	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
21385373Sjlemon	return (0);
21410665Sbde}
21510665Sbde
21610665Sbdevoid
21785373Sjlemoncnremove(struct consdev *cn)
21810665Sbde{
21985373Sjlemon	struct cn_device *cnd;
22010665Sbde
22185373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
22285373Sjlemon		if (cnd->cnd_cn != cn)
22385373Sjlemon			continue;
22485373Sjlemon		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
22585373Sjlemon		if (cnd->cnd_vp != NULL)
22685373Sjlemon			vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
22785373Sjlemon		cnd->cnd_vp = NULL;
22885373Sjlemon		cnd->cnd_cn = NULL;
22985373Sjlemon		cnd->cnd_name[0] = '\0';
23085373Sjlemon#if 0
23185373Sjlemon		/*
23285373Sjlemon		 * XXX
23385373Sjlemon		 * syscons gets really confused if console resources are
23485373Sjlemon		 * freed after the system has initialized.
23585373Sjlemon		 */
23685373Sjlemon		if (cn->cn_term != NULL)
23785373Sjlemon			cn->cn_term(cn);
23885373Sjlemon#endif
23910665Sbde		return;
24056582Sbde	}
2414Srgrimes}
2424Srgrimes
24385373Sjlemonvoid
24485373Sjlemoncnselect(struct consdev *cn)
24527982Sjulian{
24685373Sjlemon	struct cn_device *cnd;
24727982Sjulian
24885373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
24985373Sjlemon		if (cnd->cnd_cn != cn)
25085373Sjlemon			continue;
25185373Sjlemon		if (cnd == STAILQ_FIRST(&cn_devlist))
25285373Sjlemon			return;
25385373Sjlemon		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
25485373Sjlemon		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
25527982Sjulian		return;
25685373Sjlemon	}
25785373Sjlemon}
25827982Sjulian
25985373Sjlemonvoid
26085373Sjlemoncndebug(char *str)
26185373Sjlemon{
26285373Sjlemon	int i, len;
26385373Sjlemon
26485373Sjlemon	len = strlen(str);
26585373Sjlemon	cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' ');
26685373Sjlemon	for (i = 0; i < len; i++)
26785373Sjlemon		cnputc(str[i]);
26885373Sjlemon	cnputc('\n');
26985373Sjlemon}
27085373Sjlemon
27185373Sjlemonstatic int
27285373Sjlemonsysctl_kern_console(SYSCTL_HANDLER_ARGS)
27385373Sjlemon{
27485373Sjlemon	struct cn_device *cnd;
27585373Sjlemon	struct consdev *cp, **list;
27685373Sjlemon	char *name, *p;
27785373Sjlemon	int delete, len, error;
27885373Sjlemon
27985373Sjlemon	len = 2;
28085373Sjlemon	SET_FOREACH(list, cons_set) {
28185373Sjlemon		cp = *list;
28285373Sjlemon		if (cp->cn_dev != NULL)
28385373Sjlemon			len += strlen(devtoname(cp->cn_dev)) + 1;
28456582Sbde	}
28585373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
28685373Sjlemon		len += strlen(devtoname(cnd->cnd_cn->cn_dev)) + 1;
28785373Sjlemon	len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
288111119Simp	MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
28985373Sjlemon	p = name;
29085373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
29185373Sjlemon		p += sprintf(p, "%s,", devtoname(cnd->cnd_cn->cn_dev));
29285373Sjlemon	*p++ = '/';
29385373Sjlemon	SET_FOREACH(list, cons_set) {
29485373Sjlemon		cp = *list;
29585373Sjlemon		if (cp->cn_dev != NULL)
29685373Sjlemon			p += sprintf(p, "%s,", devtoname(cp->cn_dev));
29785373Sjlemon	}
29885373Sjlemon	error = sysctl_handle_string(oidp, name, len, req);
29985373Sjlemon	if (error == 0 && req->newptr != NULL) {
30085373Sjlemon		p = name;
30185373Sjlemon		error = ENXIO;
30285373Sjlemon		delete = 0;
30385373Sjlemon		if (*p == '-') {
30485373Sjlemon			delete = 1;
30585373Sjlemon			p++;
30685373Sjlemon		}
30785373Sjlemon		SET_FOREACH(list, cons_set) {
30885373Sjlemon			cp = *list;
30985373Sjlemon			if (cp->cn_dev == NULL ||
31085373Sjlemon			    strcmp(p, devtoname(cp->cn_dev)) != 0)
31185373Sjlemon				continue;
31285373Sjlemon			if (delete) {
31385373Sjlemon				cnremove(cp);
31485373Sjlemon				error = 0;
31585373Sjlemon			} else {
31685373Sjlemon				error = cnadd(cp);
31785373Sjlemon				if (error == 0)
31885373Sjlemon					cnselect(cp);
31985373Sjlemon			}
32085373Sjlemon			break;
32185373Sjlemon		}
32285373Sjlemon	}
32385373Sjlemon	FREE(name, M_TEMP);
32485373Sjlemon	return (error);
32527982Sjulian}
32627982Sjulian
32785373SjlemonSYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
32885373Sjlemon	0, 0, sysctl_kern_console, "A", "Console device control");
32985373Sjlemon
33027982Sjulian/*
33127982Sjulian * User has changed the state of the console muting.
33227982Sjulian * This may require us to open or close the device in question.
33327982Sjulian */
33412675Sjulianstatic int
33562573Sphksysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
33627982Sjulian{
33727982Sjulian	int error;
33827982Sjulian	int ocn_mute;
33927982Sjulian
34027982Sjulian	ocn_mute = cn_mute;
34127982Sjulian	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
34285373Sjlemon	if (error != 0 || req->newptr == NULL)
34385373Sjlemon		return (error);
34485373Sjlemon	if (ocn_mute && !cn_mute && cn_is_open)
34585373Sjlemon		error = cnopen(NODEV, openflag, 0, curthread);
34685373Sjlemon	else if (!ocn_mute && cn_mute && cn_is_open) {
34785373Sjlemon		error = cnclose(NODEV, openflag, 0, curthread);
34885373Sjlemon		cn_is_open = 1;		/* XXX hack */
34927982Sjulian	}
35027982Sjulian	return (error);
35127982Sjulian}
35227982Sjulian
35327982SjulianSYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
35485373Sjlemon	0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
35527982Sjulian
35627982Sjulianstatic int
35785373Sjlemoncn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
35885373Sjlemon{
35985373Sjlemon	char path[CNDEVPATHMAX];
36085884Sjlemon	struct nameidata nd;
36185884Sjlemon	struct vnode *vp;
3624Srgrimes	dev_t dev;
36385373Sjlemon	int error;
3641007Sdg
36585884Sjlemon	if ((vp = cnd->cnd_vp) != NULL) {
36685884Sjlemon		if (!forceopen && vp->v_type != VBAD) {
36785884Sjlemon			dev = vp->v_rdev;
36885373Sjlemon			return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
36927982Sjulian		}
37085373Sjlemon		cnd->cnd_vp = NULL;
37191406Sjhb		vn_close(vp, openflag, td->td_ucred, td);
3725764Sbde	}
373105354Srobert	if (cnd->cnd_name[0] == '\0') {
374105354Srobert		strlcpy(cnd->cnd_name, devtoname(cnd->cnd_cn->cn_dev),
37585373Sjlemon		    sizeof(cnd->cnd_name));
376105354Srobert	}
37785373Sjlemon	snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_name);
37885373Sjlemon	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
37985373Sjlemon	error = vn_open(&nd, &openflag, 0);
38085373Sjlemon	if (error == 0) {
38185373Sjlemon		NDFREE(&nd, NDF_ONLY_PNBUF);
38285373Sjlemon		VOP_UNLOCK(nd.ni_vp, 0, td);
38385373Sjlemon		if (nd.ni_vp->v_type == VCHR)
38485373Sjlemon			cnd->cnd_vp = nd.ni_vp;
38585373Sjlemon		else
38691406Sjhb			vn_close(nd.ni_vp, openflag, td->td_ucred, td);
38785373Sjlemon	}
38885373Sjlemon	return (cnd->cnd_vp != NULL);
3894Srgrimes}
3908876Srgrimes
39112675Sjulianstatic int
39285373Sjlemoncnopen(dev_t dev, int flag, int mode, struct thread *td)
3934Srgrimes{
39485373Sjlemon	struct cn_device *cnd;
3951007Sdg
39685448Sjlemon	openflag = flag | FWRITE;	/* XXX */
39785373Sjlemon	cn_is_open = 1;			/* console is logically open */
39885373Sjlemon	if (cn_mute)
3994Srgrimes		return (0);
40085373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
40185373Sjlemon		cn_devopen(cnd, td, 0);
40285373Sjlemon	return (0);
40385373Sjlemon}
40485373Sjlemon
40585373Sjlemonstatic int
40685373Sjlemoncnclose(dev_t dev, int flag, int mode, struct thread *td)
40785373Sjlemon{
40885373Sjlemon	struct cn_device *cnd;
40985458Sjlemon	struct vnode *vp;
41085373Sjlemon
41185373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
41285458Sjlemon		if ((vp = cnd->cnd_vp) == NULL)
41385373Sjlemon			continue;
41485373Sjlemon		cnd->cnd_vp = NULL;
41591406Sjhb		vn_close(vp, openflag, td->td_ucred, td);
4165764Sbde	}
41785373Sjlemon	cn_is_open = 0;
41827982Sjulian	return (0);
4194Srgrimes}
4208876Srgrimes
42112675Sjulianstatic int
42285373Sjlemoncnread(dev_t dev, struct uio *uio, int flag)
4234Srgrimes{
42485373Sjlemon	struct cn_device *cnd;
42556582Sbde
42685373Sjlemon	cnd = STAILQ_FIRST(&cn_devlist);
42785373Sjlemon	if (cn_mute || CND_INVALID(cnd, curthread))
4284Srgrimes		return (0);
42985373Sjlemon	dev = cnd->cnd_vp->v_rdev;
43046676Sphk	return ((*devsw(dev)->d_read)(dev, uio, flag));
4314Srgrimes}
4328876Srgrimes
43312675Sjulianstatic int
43485373Sjlemoncnwrite(dev_t dev, struct uio *uio, int flag)
4354Srgrimes{
43685373Sjlemon	struct cn_device *cnd;
43756582Sbde
43885373Sjlemon	cnd = STAILQ_FIRST(&cn_devlist);
43985373Sjlemon	if (cn_mute || CND_INVALID(cnd, curthread))
44085373Sjlemon		goto done;
4411021Sdg	if (constty)
4424Srgrimes		dev = constty->t_dev;
4434Srgrimes	else
44485373Sjlemon		dev = cnd->cnd_vp->v_rdev;
44585373Sjlemon	if (dev != NULL) {
44685373Sjlemon		log_console(uio);
44785373Sjlemon		return ((*devsw(dev)->d_write)(dev, uio, flag));
44885373Sjlemon	}
44985373Sjlemondone:
45085373Sjlemon	uio->uio_resid = 0; /* dump the data */
45185373Sjlemon	return (0);
4524Srgrimes}
4538876Srgrimes
45412675Sjulianstatic int
45585373Sjlemoncnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
4564Srgrimes{
45785373Sjlemon	struct cn_device *cnd;
4584Srgrimes	int error;
4594Srgrimes
46085373Sjlemon	cnd = STAILQ_FIRST(&cn_devlist);
46185373Sjlemon	if (cn_mute || CND_INVALID(cnd, td))
4624Srgrimes		return (0);
4634Srgrimes	/*
4644Srgrimes	 * Superuser can always use this to wrest control of console
4654Srgrimes	 * output from the "virtual" console.
4664Srgrimes	 */
4674Srgrimes	if (cmd == TIOCCONS && constty) {
46893593Sjhb		error = suser(td);
4694Srgrimes		if (error)
4704Srgrimes			return (error);
4714Srgrimes		constty = NULL;
4724Srgrimes		return (0);
4734Srgrimes	}
47485373Sjlemon	dev = cnd->cnd_vp->v_rdev;
47585373Sjlemon	if (dev != NULL)
47685373Sjlemon		return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
47785373Sjlemon	return (0);
4784Srgrimes}
4794Srgrimes
48085373Sjlemon/*
48185373Sjlemon * XXX
48285373Sjlemon * poll/kqfilter do not appear to be correct
48385373Sjlemon */
48412675Sjulianstatic int
48585373Sjlemoncnpoll(dev_t dev, int events, struct thread *td)
4864Srgrimes{
48785373Sjlemon	struct cn_device *cnd;
4886712Spst
48985373Sjlemon	cnd = STAILQ_FIRST(&cn_devlist);
49085373Sjlemon	if (cn_mute || CND_INVALID(cnd, td))
49185373Sjlemon		return (0);
49285373Sjlemon	dev = cnd->cnd_vp->v_rdev;
49385373Sjlemon	if (dev != NULL)
49485373Sjlemon		return ((*devsw(dev)->d_poll)(dev, events, td));
49585373Sjlemon	return (0);
4964Srgrimes}
4974Srgrimes
49872521Sjlemonstatic int
49985373Sjlemoncnkqfilter(dev_t dev, struct knote *kn)
50072521Sjlemon{
50185373Sjlemon	struct cn_device *cnd;
50285373Sjlemon
50385373Sjlemon	cnd = STAILQ_FIRST(&cn_devlist);
50485373Sjlemon	if (cn_mute || CND_INVALID(cnd, curthread))
50572521Sjlemon		return (1);
50685373Sjlemon	dev = cnd->cnd_vp->v_rdev;
50785373Sjlemon	if (dev != NULL)
50872521Sjlemon		return ((*devsw(dev)->d_kqfilter)(dev, kn));
50972521Sjlemon	return (1);
51072521Sjlemon}
51172521Sjlemon
51285373Sjlemon/*
51385373Sjlemon * Low level console routines.
51485373Sjlemon */
515798Swollmanint
51685373Sjlemoncngetc(void)
5174Srgrimes{
5185160Sjoerg	int c;
51985373Sjlemon
52085373Sjlemon	if (cn_mute)
52119268Sjulian		return (-1);
52285373Sjlemon	while ((c = cncheckc()) == -1)
52385373Sjlemon		;
52485373Sjlemon	if (c == '\r')
52585373Sjlemon		c = '\n';		/* console input is always ICRNL */
5265160Sjoerg	return (c);
5274Srgrimes}
5284Srgrimes
5293728Sphkint
53085373Sjlemoncncheckc(void)
5313728Sphk{
53285373Sjlemon	struct cn_device *cnd;
53385373Sjlemon	struct consdev *cn;
53485373Sjlemon	int c;
53585373Sjlemon
53685373Sjlemon	if (cn_mute)
53718287Sbde		return (-1);
53885373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
53985373Sjlemon		cn = cnd->cnd_cn;
540111194Sphk		c = cn->cn_checkc(cn);
54185373Sjlemon		if (c != -1) {
54285373Sjlemon			return (c);
54385373Sjlemon		}
54485373Sjlemon	}
54585373Sjlemon	return (-1);
5463728Sphk}
5473728Sphk
548798Swollmanvoid
54985373Sjlemoncnputc(int c)
5504Srgrimes{
55185373Sjlemon	struct cn_device *cnd;
55285373Sjlemon	struct consdev *cn;
55387620Sguido	char *cp;
55485373Sjlemon
55585373Sjlemon	if (cn_mute || c == '\0')
5564Srgrimes		return;
55785373Sjlemon	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
55885373Sjlemon		cn = cnd->cnd_cn;
5594Srgrimes		if (c == '\n')
560111194Sphk			cn->cn_putc(cn, '\r');
561111194Sphk		cn->cn_putc(cn, c);
5624Srgrimes	}
56387649Sguido#ifdef DDB
56487620Sguido	if (console_pausing && !db_active && (c == '\n')) {
56587649Sguido#else
56687649Sguido	if (console_pausing && (c == '\n')) {
56787649Sguido#endif
56887620Sguido		for (cp = console_pausestr; *cp != '\0'; cp++)
56987620Sguido			cnputc(*cp);
57087620Sguido		if (cngetc() == '.')
57187620Sguido			console_pausing = 0;
57287620Sguido		cnputc('\r');
57387620Sguido		for (cp = console_pausestr; *cp != '\0'; cp++)
57487620Sguido			cnputc(' ');
57587620Sguido		cnputc('\r');
57687620Sguido	}
5774Srgrimes}
5784Srgrimes
57955823Syokotavoid
58085373Sjlemoncndbctl(int on)
58155823Syokota{
58285373Sjlemon	struct cn_device *cnd;
58385373Sjlemon	struct consdev *cn;
58455823Syokota	static int refcount;
58555823Syokota
58655823Syokota	if (!on)
58755823Syokota		refcount--;
58885373Sjlemon	if (refcount == 0)
58985373Sjlemon		STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
59085373Sjlemon			cn = cnd->cnd_cn;
59185373Sjlemon			if (cn->cn_dbctl != NULL)
592111194Sphk				cn->cn_dbctl(cn, on);
59385373Sjlemon		}
59455823Syokota	if (on)
59555823Syokota		refcount++;
59655823Syokota}
597112046Sphk
598116663Siedowsestatic int consmsgbuf_size = 8192;
599116663SiedowseSYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
600116663Siedowse    "");
601116663Siedowse
602116663Siedowse/*
603116663Siedowse * Redirect console output to a tty.
604116663Siedowse */
605116663Siedowsevoid
606116663Siedowseconstty_set(struct tty *tp)
607116663Siedowse{
608116663Siedowse	int size;
609116663Siedowse
610116663Siedowse	KASSERT(tp != NULL, ("constty_set: NULL tp"));
611116663Siedowse	if (consbuf == NULL) {
612116663Siedowse		size = consmsgbuf_size;
613116663Siedowse		consbuf = malloc(size, M_TTYS, M_WAITOK);
614116663Siedowse		msgbuf_init(&consmsgbuf, consbuf, size);
615116663Siedowse		callout_init(&conscallout, 0);
616116663Siedowse	}
617116663Siedowse	constty = tp;
618116663Siedowse	constty_timeout(NULL);
619116663Siedowse}
620116663Siedowse
621116663Siedowse/*
622116663Siedowse * Disable console redirection to a tty.
623116663Siedowse */
624116663Siedowsevoid
625116663Siedowseconstty_clear(void)
626116663Siedowse{
627116663Siedowse	int c;
628116663Siedowse
629116663Siedowse	constty = NULL;
630116663Siedowse	if (consbuf == NULL)
631116663Siedowse		return;
632116663Siedowse	callout_stop(&conscallout);
633116663Siedowse	while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
634116663Siedowse		cnputc(c);
635116663Siedowse	free(consbuf, M_TTYS);
636116663Siedowse	consbuf = NULL;
637116663Siedowse}
638116663Siedowse
639116663Siedowse/* Times per second to check for pending console tty messages. */
640116663Siedowsestatic int constty_wakeups_per_second = 5;
641116663SiedowseSYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
642116663Siedowse    &constty_wakeups_per_second, 0, "");
643116663Siedowse
644112046Sphkstatic void
645116663Siedowseconstty_timeout(void *arg)
646116663Siedowse{
647116663Siedowse	int c;
648116663Siedowse
649116663Siedowse	while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) {
650116663Siedowse		if (tputchar(c, constty) < 0)
651116663Siedowse			constty = NULL;
652116663Siedowse	}
653116663Siedowse	if (constty != NULL) {
654116663Siedowse		callout_reset(&conscallout, hz / constty_wakeups_per_second,
655116663Siedowse		    constty_timeout, NULL);
656116663Siedowse	} else {
657116663Siedowse		/* Deallocate the constty buffer memory. */
658116663Siedowse		constty_clear();
659116663Siedowse	}
660116663Siedowse}
661116663Siedowse
662116663Siedowsestatic void
663112046Sphkcn_drvinit(void *unused)
664112046Sphk{
665112046Sphk
666112046Sphk	make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console");
667112046Sphk}
668112046Sphk
669112046SphkSYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL)
670