File:  [DragonFly] / src / sys / kern / tty_cons.c
Revision 1.12: download - view: text, annotated - select for diffs
Thu May 13 23:49:23 2004 UTC (10 years, 7 months ago) by dillon
Branches: MAIN
CVS tags: HEAD
device switch 1/many: Remove d_autoq, add d_clone (where d_autoq was).

d_autoq was used to allow the device port dispatch to mix old-style synchronous
calls with new style messaging calls within a particular device.  It was never
used for that purpose.

d_clone will be more fully implemented as work continues.  We are going to
install d_port in the dev_t (struct specinfo) structure itself and d_clone
will be needed to allow devices to 'revector' the port on a minor-number
by minor-number basis, in particular allowing minor numbers to be directly
dispatched to distinct threads.  This is something we will be needing later
on.

    1: /*
    2:  * Copyright (c) 1988 University of Utah.
    3:  * Copyright (c) 1991 The Regents of the University of California.
    4:  * All rights reserved.
    5:  *
    6:  * This code is derived from software contributed to Berkeley by
    7:  * the Systems Programming Group of the University of Utah Computer
    8:  * Science Department.
    9:  *
   10:  * Redistribution and use in source and binary forms, with or without
   11:  * modification, are permitted provided that the following conditions
   12:  * are met:
   13:  * 1. Redistributions of source code must retain the above copyright
   14:  *    notice, this list of conditions and the following disclaimer.
   15:  * 2. Redistributions in binary form must reproduce the above copyright
   16:  *    notice, this list of conditions and the following disclaimer in the
   17:  *    documentation and/or other materials provided with the distribution.
   18:  * 3. All advertising materials mentioning features or use of this software
   19:  *    must display the following acknowledgement:
   20:  *	This product includes software developed by the University of
   21:  *	California, Berkeley and its contributors.
   22:  * 4. Neither the name of the University nor the names of its contributors
   23:  *    may be used to endorse or promote products derived from this software
   24:  *    without specific prior written permission.
   25:  *
   26:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   27:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   28:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   29:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   30:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   31:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   32:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   33:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   34:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   35:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   36:  * SUCH DAMAGE.
   37:  *
   38:  *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
   39:  * $FreeBSD: src/sys/kern/tty_cons.c,v 1.81.2.4 2001/12/17 18:44:41 guido Exp $
   40:  * $DragonFly: src/sys/kern/tty_cons.c,v 1.12 2004/05/13 23:49:23 dillon Exp $
   41:  */
   42: 
   43: #include "opt_ddb.h"
   44: 
   45: #include <sys/param.h>
   46: #include <sys/systm.h>
   47: #include <sys/conf.h>
   48: #include <sys/cons.h>
   49: #include <sys/kernel.h>
   50: #include <sys/proc.h>
   51: #include <sys/reboot.h>
   52: #include <sys/sysctl.h>
   53: #include <sys/tty.h>
   54: #include <sys/uio.h>
   55: #include <sys/msgport.h>
   56: #include <sys/msgport2.h>
   57: #include <sys/device.h>
   58: 
   59: #include <ddb/ddb.h>
   60: 
   61: #include <machine/cpu.h>
   62: 
   63: static	d_open_t	cnopen;
   64: static	d_close_t	cnclose;
   65: static	d_read_t	cnread;
   66: static	d_write_t	cnwrite;
   67: static	d_ioctl_t	cnioctl;
   68: static	d_poll_t	cnpoll;
   69: static	d_kqfilter_t	cnkqfilter;
   70: static int console_putport(lwkt_port_t port, lwkt_msg_t lmsg);
   71: 
   72: #define	CDEV_MAJOR	0
   73: static struct cdevsw cn_cdevsw = {
   74: 	/* name */	"console",
   75: 	/* maj */	CDEV_MAJOR,
   76: 	/* flags */	D_TTY | D_KQFILTER,
   77: 	/* port */	NULL,
   78: 	/* clone */	NULL,
   79: 
   80: 	/* open */	cnopen,
   81: 	/* close */	cnclose,
   82: 	/* read */	cnread,
   83: 	/* write */	cnwrite,
   84: 	/* ioctl */	cnioctl,
   85: 	/* poll */	cnpoll,
   86: 	/* mmap */	nommap,
   87: 	/* strategy */	nostrategy,
   88: 	/* dump */	nodump,
   89: 	/* psize */	nopsize,
   90: 	/* kqfilter */	cnkqfilter
   91: };
   92: 
   93: static dev_t	cn_dev_t; 	/* seems to be never really used */
   94: static udev_t	cn_udev_t;
   95: SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
   96: 	&cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
   97: 
   98: static int cn_mute;
   99: 
  100: int	cons_unavail = 0;	/* XXX:
  101: 				 * physical console not available for
  102: 				 * input (i.e., it is in graphics mode)
  103: 				 */
  104: 
  105: static u_char cn_is_open;		/* nonzero if logical console is open */
  106: static int openmode, openflag;		/* how /dev/console was openned */
  107: static dev_t cn_devfsdev;		/* represents the device private info */
  108: static u_char cn_phys_is_open;		/* nonzero if physical device is open */
  109:        struct consdev *cn_tab;		/* physical console device info */
  110: static u_char console_pausing;		/* pause after each line during probe */
  111: static char *console_pausestr=
  112: "<pause; press any key to proceed to next line or '.' to end pause mode>";
  113: 
  114: static lwkt_port_t	cn_fwd_port;
  115: static struct lwkt_port	cn_port;
  116: 
  117: 
  118: CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  119: SET_DECLARE(cons_set, struct consdev);
  120: 
  121: void
  122: cninit()
  123: {
  124: 	struct consdev *best_cp, *cp, **list;
  125: 
  126: 	/*
  127: 	 * Our port intercept
  128: 	 */
  129: 	lwkt_initport(&cn_port, NULL);
  130: 	cn_port.mp_putport = console_putport;
  131: 
  132: 	/*
  133: 	 * Find the first console with the highest priority.
  134: 	 */
  135: 	best_cp = NULL;
  136: 	SET_FOREACH(list, cons_set) {
  137: 		cp = *list;
  138: 		if (cp->cn_probe == NULL)
  139: 			continue;
  140: 		(*cp->cn_probe)(cp);
  141: 		if (cp->cn_pri > CN_DEAD &&
  142: 		    (best_cp == NULL || cp->cn_pri > best_cp->cn_pri))
  143: 			best_cp = cp;
  144: 	}
  145: 
  146: 	/*
  147: 	 * Check if we should mute the console (for security reasons perhaps)
  148: 	 * It can be changes dynamically using sysctl kern.consmute
  149: 	 * once we are up and going.
  150: 	 * 
  151: 	 */
  152:         cn_mute = ((boothowto & (RB_MUTE
  153: 			|RB_SINGLE
  154: 			|RB_VERBOSE
  155: 			|RB_ASKNAME
  156: 			|RB_CONFIG)) == RB_MUTE);
  157: 	
  158: 	/*
  159: 	 * If no console, give up.
  160: 	 */
  161: 	if (best_cp == NULL) {
  162: 		if (cn_tab != NULL && cn_tab->cn_term != NULL)
  163: 			(*cn_tab->cn_term)(cn_tab);
  164: 		cn_tab = best_cp;
  165: 		return;
  166: 	}
  167: 
  168: 	/*
  169: 	 * Initialize console, then attach to it.  This ordering allows
  170: 	 * debugging using the previous console, if any.
  171: 	 */
  172: 	(*best_cp->cn_init)(best_cp);
  173: 	if (cn_tab != NULL && cn_tab != best_cp) {
  174: 		/* Turn off the previous console.  */
  175: 		if (cn_tab->cn_term != NULL)
  176: 			(*cn_tab->cn_term)(cn_tab);
  177: 	}
  178: 	if (boothowto & RB_PAUSE)
  179: 		console_pausing = 1;
  180: 	cn_tab = best_cp;
  181: }
  182: 
  183: void
  184: cninit_finish()
  185: {
  186: 	if ((cn_tab == NULL) || cn_mute)
  187: 		return;
  188: 
  189: 	/*
  190: 	 * Hook the open and close functions.
  191: 	 */
  192: 	if (dev_dport(cn_tab->cn_dev))
  193: 		cn_fwd_port = cdevsw_dev_override(cn_tab->cn_dev, &cn_port);
  194: 	cn_dev_t = cn_tab->cn_dev;
  195: 	cn_udev_t = dev2udev(cn_dev_t);
  196: 	console_pausing = 0;
  197: }
  198: 
  199: static void
  200: cnuninit(void)
  201: {
  202: 	if (cn_tab == NULL)
  203: 		return;
  204: 
  205: 	/*
  206: 	 * Unhook the open and close functions.
  207: 	 */
  208: 	cdevsw_dev_override(cn_tab->cn_dev, NULL);
  209: 	cn_fwd_port = NULL;
  210: 	cn_dev_t = NODEV;
  211: 	cn_udev_t = NOUDEV;
  212: }
  213: 
  214: /*
  215:  * User has changed the state of the console muting.
  216:  * This may require us to open or close the device in question.
  217:  */
  218: static int
  219: sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
  220: {
  221: 	int error;
  222: 	int ocn_mute;
  223: 
  224: 	ocn_mute = cn_mute;
  225: 	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
  226: 	if((error == 0) && (cn_tab != NULL) && (req->newptr != NULL)) {
  227: 		if(ocn_mute && !cn_mute) {
  228: 			/*
  229: 			 * going from muted to unmuted.. open the physical dev 
  230: 			 * if the console has been openned
  231: 			 */
  232: 			cninit_finish();
  233: 			if(cn_is_open)
  234: 				/* XXX curproc is not what we want really */
  235: 				error = cnopen(cn_dev_t, openflag,
  236: 					openmode, curthread);
  237: 			/* if it failed, back it out */
  238: 			if ( error != 0) cnuninit();
  239: 		} else if (!ocn_mute && cn_mute) {
  240: 			/*
  241: 			 * going from unmuted to muted.. close the physical dev 
  242: 			 * if it's only open via /dev/console
  243: 			 */
  244: 			if(cn_is_open)
  245: 				error = cnclose(cn_dev_t, openflag,
  246: 					openmode, curthread);
  247: 			if ( error == 0) cnuninit();
  248: 		}
  249: 		if (error != 0) {
  250: 			/* 
  251: 	 		 * back out the change if there was an error
  252: 			 */
  253: 			cn_mute = ocn_mute;
  254: 		}
  255: 	}
  256: 	return (error);
  257: }
  258: 
  259: SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
  260: 	0, sizeof cn_mute, sysctl_kern_consmute, "I", "");
  261: 
  262: static int
  263: console_putport(lwkt_port_t port, lwkt_msg_t lmsg)
  264: {
  265: 	cdevallmsg_t msg = (cdevallmsg_t)lmsg;
  266: 	int error;
  267: 
  268: 	switch(msg->am_lmsg.ms_cmd.cm_op) {
  269: 	case CDEV_CMD_OPEN:
  270: 		error = cnopen(
  271: 			    msg->am_open.msg.dev,
  272: 			    msg->am_open.oflags,
  273: 			    msg->am_open.devtype,
  274: 			    msg->am_open.td
  275: 			);
  276: 		break;
  277: 	case CDEV_CMD_CLOSE:
  278: 		error = cnclose(
  279: 			    msg->am_close.msg.dev,
  280: 			    msg->am_close.fflag,
  281: 			    msg->am_close.devtype,
  282: 			    msg->am_close.td
  283: 			);
  284: 		break;
  285: 	default:
  286: 		 error = lwkt_forwardmsg(cn_fwd_port, &msg->am_lmsg);
  287: 		 break;
  288: 	}
  289: 	return(error);
  290: }
  291: 
  292: static int
  293: cnopen(dev, flag, mode, td)
  294: 	dev_t dev;
  295: 	int flag, mode;
  296: 	struct thread *td;
  297: {
  298: 	dev_t cndev, physdev;
  299: 	int retval = 0;
  300: 
  301: 	if (cn_tab == NULL || cn_fwd_port == NULL)
  302: 		return (0);
  303: 	cndev = cn_tab->cn_dev;
  304: 	physdev = (major(dev) == major(cndev) ? dev : cndev);
  305: 	/*
  306: 	 * If mute is active, then non console opens don't get here
  307: 	 * so we don't need to check for that. They 
  308: 	 * bypass this and go straight to the device.
  309: 	 */
  310: 	if(!cn_mute)
  311: 		retval = dev_port_dopen(cn_fwd_port, physdev, flag, mode, td);
  312: 	if (retval == 0) {
  313: 		/* 
  314: 		 * check if we openned it via /dev/console or 
  315: 		 * via the physical entry (e.g. /dev/sio0).
  316: 		 */
  317: 		if (dev == cndev)
  318: 			cn_phys_is_open = 1;
  319: 		else if (physdev == cndev) {
  320: 			openmode = mode;
  321: 			openflag = flag;
  322: 			cn_is_open = 1;
  323: 		}
  324: 		dev->si_tty = physdev->si_tty;
  325: 	}
  326: 	return (retval);
  327: }
  328: 
  329: static int
  330: cnclose(dev, flag, mode, td)
  331: 	dev_t dev;
  332: 	int flag, mode;
  333: 	struct thread *td;
  334: {
  335: 	dev_t cndev;
  336: 	struct tty *cn_tp;
  337: 
  338: 	if (cn_tab == NULL || cn_fwd_port == NULL)
  339: 		return (0);
  340: 	cndev = cn_tab->cn_dev;
  341: 	cn_tp = cndev->si_tty;
  342: 	/*
  343: 	 * act appropriatly depending on whether it's /dev/console
  344: 	 * or the pysical device (e.g. /dev/sio) that's being closed.
  345: 	 * in either case, don't actually close the device unless
  346: 	 * both are closed.
  347: 	 */
  348: 	if (dev == cndev) {
  349: 		/* the physical device is about to be closed */
  350: 		cn_phys_is_open = 0;
  351: 		if (cn_is_open) {
  352: 			if (cn_tp) {
  353: 				/* perform a ttyhalfclose() */
  354: 				/* reset session and proc group */
  355: 				cn_tp->t_pgrp = NULL;
  356: 				cn_tp->t_session = NULL;
  357: 			}
  358: 			return (0);
  359: 		}
  360: 	} else if (major(dev) != major(cndev)) {
  361: 		/* the logical console is about to be closed */
  362: 		cn_is_open = 0;
  363: 		if (cn_phys_is_open)
  364: 			return (0);
  365: 		dev = cndev;
  366: 	}
  367: 	if (cn_fwd_port)
  368: 		return (dev_port_dclose(cn_fwd_port, dev, flag, mode, td));
  369: 	return (0);
  370: }
  371: 
  372: static int
  373: cnread(dev, uio, flag)
  374: 	dev_t dev;
  375: 	struct uio *uio;
  376: 	int flag;
  377: {
  378: 
  379: 	if (cn_tab == NULL || cn_fwd_port == NULL)
  380: 		return (0);
  381: 	dev = cn_tab->cn_dev;
  382: 	return (dev_port_dread(cn_fwd_port, dev, uio, flag));
  383: }
  384: 
  385: static int
  386: cnwrite(dev, uio, flag)
  387: 	dev_t dev;
  388: 	struct uio *uio;
  389: 	int flag;
  390: {
  391: 
  392: 	if (cn_tab == NULL || cn_fwd_port == NULL) {
  393: 		uio->uio_resid = 0; /* dump the data */
  394: 		return (0);
  395: 	}
  396: 	if (constty)
  397: 		dev = constty->t_dev;
  398: 	else
  399: 		dev = cn_tab->cn_dev;
  400: 	log_console(uio);
  401: 	return (dev_port_dwrite(cn_fwd_port, dev, uio, flag));
  402: }
  403: 
  404: static int
  405: cnioctl(dev, cmd, data, flag, td)
  406: 	dev_t dev;
  407: 	u_long cmd;
  408: 	caddr_t data;
  409: 	int flag;
  410: 	struct thread *td;
  411: {
  412: 	int error;
  413: 
  414: 	if (cn_tab == NULL || cn_fwd_port == NULL)
  415: 		return (0);
  416: 	KKASSERT(td->td_proc != NULL);
  417: 	/*
  418: 	 * Superuser can always use this to wrest control of console
  419: 	 * output from the "virtual" console.
  420: 	 */
  421: 	if (cmd == TIOCCONS && constty) {
  422: 		error = suser(td);
  423: 		if (error)
  424: 			return (error);
  425: 		constty = NULL;
  426: 		return (0);
  427: 	}
  428: 	dev = cn_tab->cn_dev;
  429: 	return (dev_port_dioctl(cn_fwd_port, dev, cmd, data, flag, td));
  430: }
  431: 
  432: static int
  433: cnpoll(dev, events, td)
  434: 	dev_t dev;
  435: 	int events;
  436: 	struct thread *td;
  437: {
  438: 	if ((cn_tab == NULL) || cn_mute || cn_fwd_port == NULL)
  439: 		return (1);
  440: 
  441: 	dev = cn_tab->cn_dev;
  442: 
  443: 	return (dev_port_dpoll(cn_fwd_port, dev, events, td));
  444: }
  445: 
  446: static int
  447: cnkqfilter(dev, kn)
  448: 	dev_t dev;
  449: 	struct knote *kn;
  450: {
  451: 	if ((cn_tab == NULL) || cn_mute || cn_fwd_port == NULL)
  452: 		return (1);
  453: 
  454: 	dev = cn_tab->cn_dev;
  455: 	return (dev_port_dkqfilter(cn_fwd_port, dev, kn));
  456: }
  457: 
  458: int
  459: cngetc()
  460: {
  461: 	int c;
  462: 	if ((cn_tab == NULL) || cn_mute)
  463: 		return (-1);
  464: 	c = (*cn_tab->cn_getc)(cn_tab->cn_dev);
  465: 	if (c == '\r') c = '\n'; /* console input is always ICRNL */
  466: 	return (c);
  467: }
  468: 
  469: int
  470: cncheckc()
  471: {
  472: 	if ((cn_tab == NULL) || cn_mute)
  473: 		return (-1);
  474: 	return ((*cn_tab->cn_checkc)(cn_tab->cn_dev));
  475: }
  476: 
  477: void
  478: cnputc(c)
  479: 	int c;
  480: {
  481: 	char *cp;
  482: 
  483: 	if ((cn_tab == NULL) || cn_mute)
  484: 		return;
  485: 	if (c) {
  486: 		if (c == '\n')
  487: 			(*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
  488: 		(*cn_tab->cn_putc)(cn_tab->cn_dev, c);
  489: #ifdef DDB
  490: 		if (console_pausing && !db_active && (c == '\n')) {
  491: #else
  492: 		if (console_pausing && (c == '\n')) {
  493: #endif
  494: 			for(cp=console_pausestr; *cp != '\0'; cp++)
  495: 			    (*cn_tab->cn_putc)(cn_tab->cn_dev, *cp);
  496: 			if (cngetc() == '.')
  497: 				console_pausing = 0;
  498: 			(*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
  499: 			for(cp=console_pausestr; *cp != '\0'; cp++)
  500: 			    (*cn_tab->cn_putc)(cn_tab->cn_dev, ' ');
  501: 			(*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
  502: 		}
  503: 	}
  504: }
  505: 
  506: void
  507: cndbctl(on)
  508: 	int on;
  509: {
  510: 	static int refcount;
  511: 
  512: 	if (cn_tab == NULL)
  513: 		return;
  514: 	if (!on)
  515: 		refcount--;
  516: 	if (refcount == 0 && cn_tab->cn_dbctl != NULL)
  517: 		(*cn_tab->cn_dbctl)(cn_tab->cn_dev, on);
  518: 	if (on)
  519: 		refcount++;
  520: }
  521: 
  522: static void
  523: cn_drvinit(void *unused)
  524: {
  525: 
  526: 	cn_devfsdev = make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
  527: 	    "console");
  528: }
  529: 
  530: SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)