summaryrefslogtreecommitdiffstats
path: root/sys/kern/tty_cons.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/tty_cons.c')
-rw-r--r--sys/kern/tty_cons.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/sys/kern/tty_cons.c b/sys/kern/tty_cons.c
new file mode 100644
index 0000000..080e1aa
--- /dev/null
+++ b/sys/kern/tty_cons.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)cons.c 7.2 (Berkeley) 5/9/91
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/cons.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+#include <sys/tty.h>
+#include <sys/uio.h>
+
+#include <machine/cpu.h>
+
+static d_open_t cnopen;
+static d_close_t cnclose;
+static d_read_t cnread;
+static d_write_t cnwrite;
+static d_ioctl_t cnioctl;
+static d_poll_t cnpoll;
+
+#define CDEV_MAJOR 0
+static struct cdevsw cn_cdevsw = {
+ /* open */ cnopen,
+ /* close */ cnclose,
+ /* read */ cnread,
+ /* write */ cnwrite,
+ /* ioctl */ cnioctl,
+ /* poll */ cnpoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "console",
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ D_TTY,
+ /* bmaj */ -1
+};
+
+static dev_t cn_dev_t; /* seems to be never really used */
+static udev_t cn_udev_t;
+SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
+ &cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
+
+static int cn_mute;
+
+int cons_unavail = 0; /* XXX:
+ * physical console not available for
+ * input (i.e., it is in graphics mode)
+ */
+
+static u_char cn_is_open; /* nonzero if logical console is open */
+static int openmode, openflag; /* how /dev/console was openned */
+static dev_t cn_devfsdev; /* represents the device private info */
+static u_char cn_phys_is_open; /* nonzero if physical device is open */
+static d_close_t *cn_phys_close; /* physical device close function */
+static d_open_t *cn_phys_open; /* physical device open function */
+ struct consdev *cn_tab; /* physical console device info */
+
+CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+void
+cninit()
+{
+ struct consdev *best_cp, *cp, **list;
+
+ /*
+ * Find the first console with the highest priority.
+ */
+ best_cp = NULL;
+ list = (struct consdev **)cons_set.ls_items;
+ while ((cp = *list++) != NULL) {
+ if (cp->cn_probe == NULL)
+ continue;
+ (*cp->cn_probe)(cp);
+ if (cp->cn_pri > CN_DEAD &&
+ (best_cp == NULL || cp->cn_pri > best_cp->cn_pri))
+ best_cp = cp;
+ }
+
+ /*
+ * Check if we should mute the console (for security reasons perhaps)
+ * It can be changes dynamically using sysctl kern.consmute
+ * once we are up and going.
+ *
+ */
+ cn_mute = ((boothowto & (RB_MUTE
+ |RB_SINGLE
+ |RB_VERBOSE
+ |RB_ASKNAME
+ |RB_CONFIG)) == RB_MUTE);
+
+ /*
+ * If no console, give up.
+ */
+ if (best_cp == NULL) {
+ if (cn_tab != NULL && cn_tab->cn_term != NULL)
+ (*cn_tab->cn_term)(cn_tab);
+ cn_tab = best_cp;
+ return;
+ }
+
+ /*
+ * Initialize console, then attach to it. This ordering allows
+ * debugging using the previous console, if any.
+ */
+ (*best_cp->cn_init)(best_cp);
+ if (cn_tab != NULL && cn_tab != best_cp) {
+ /* Turn off the previous console. */
+ if (cn_tab->cn_term != NULL)
+ (*cn_tab->cn_term)(cn_tab);
+ }
+ cn_tab = best_cp;
+}
+
+void
+cninit_finish()
+{
+ struct cdevsw *cdp;
+
+ if ((cn_tab == NULL) || cn_mute)
+ return;
+
+ /*
+ * Hook the open and close functions.
+ */
+ cdp = devsw(cn_tab->cn_dev);
+ if (cdp != NULL) {
+ cn_phys_close = cdp->d_close;
+ cdp->d_close = cnclose;
+ cn_phys_open = cdp->d_open;
+ cdp->d_open = cnopen;
+ }
+ cn_dev_t = cn_tab->cn_dev;
+ cn_udev_t = dev2udev(cn_dev_t);
+}
+
+static void
+cnuninit(void)
+{
+ struct cdevsw *cdp;
+
+ if (cn_tab == NULL)
+ return;
+
+ /*
+ * Unhook the open and close functions.
+ */
+ cdp = devsw(cn_tab->cn_dev);
+ if (cdp != NULL) {
+ cdp->d_close = cn_phys_close;
+ cdp->d_open = cn_phys_open;
+ }
+ cn_phys_close = NULL;
+ cn_phys_open = NULL;
+ cn_dev_t = NODEV;
+ cn_udev_t = NOUDEV;
+}
+
+/*
+ * User has changed the state of the console muting.
+ * This may require us to open or close the device in question.
+ */
+static int
+sysctl_kern_consmute SYSCTL_HANDLER_ARGS
+{
+ int error;
+ int ocn_mute;
+
+ ocn_mute = cn_mute;
+ error = sysctl_handle_int(oidp, &cn_mute, 0, req);
+ if((error == 0) && (cn_tab != NULL) && (req->newptr != NULL)) {
+ if(ocn_mute && !cn_mute) {
+ /*
+ * going from muted to unmuted.. open the physical dev
+ * if the console has been openned
+ */
+ cninit_finish();
+ if(cn_is_open)
+ /* XXX curproc is not what we want really */
+ error = cnopen(cn_dev_t, openflag,
+ openmode, curproc);
+ /* if it failed, back it out */
+ if ( error != 0) cnuninit();
+ } else if (!ocn_mute && cn_mute) {
+ /*
+ * going from unmuted to muted.. close the physical dev
+ * if it's only open via /dev/console
+ */
+ if(cn_is_open)
+ error = cnclose(cn_dev_t, openflag,
+ openmode, curproc);
+ if ( error == 0) cnuninit();
+ }
+ if (error != 0) {
+ /*
+ * back out the change if there was an error
+ */
+ cn_mute = ocn_mute;
+ }
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
+ 0, sizeof cn_mute, sysctl_kern_consmute, "I", "");
+
+static int
+cnopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ dev_t cndev, physdev;
+ int retval = 0;
+
+ if (cn_tab == NULL || cn_phys_open == NULL)
+ return (0);
+ cndev = cn_tab->cn_dev;
+ physdev = (major(dev) == major(cndev) ? dev : cndev);
+ /*
+ * If mute is active, then non console opens don't get here
+ * so we don't need to check for that. They
+ * bypass this and go straight to the device.
+ */
+ if(!cn_mute)
+ retval = (*cn_phys_open)(physdev, flag, mode, p);
+ if (retval == 0) {
+ /*
+ * check if we openned it via /dev/console or
+ * via the physical entry (e.g. /dev/sio0).
+ */
+ if (dev == cndev)
+ cn_phys_is_open = 1;
+ else if (physdev == cndev) {
+ openmode = mode;
+ openflag = flag;
+ cn_is_open = 1;
+ }
+ dev->si_tty = physdev->si_tty;
+ }
+ return (retval);
+}
+
+static int
+cnclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ dev_t cndev;
+ struct tty *cn_tp;
+
+ if (cn_tab == NULL || cn_phys_open == NULL)
+ return (0);
+ cndev = cn_tab->cn_dev;
+ cn_tp = cndev->si_tty;
+ /*
+ * act appropriatly depending on whether it's /dev/console
+ * or the pysical device (e.g. /dev/sio) that's being closed.
+ * in either case, don't actually close the device unless
+ * both are closed.
+ */
+ if (dev == cndev) {
+ /* the physical device is about to be closed */
+ cn_phys_is_open = 0;
+ if (cn_is_open) {
+ if (cn_tp) {
+ /* perform a ttyhalfclose() */
+ /* reset session and proc group */
+ cn_tp->t_pgrp = NULL;
+ cn_tp->t_session = NULL;
+ }
+ return (0);
+ }
+ } else if (major(dev) != major(cndev)) {
+ /* the logical console is about to be closed */
+ cn_is_open = 0;
+ if (cn_phys_is_open)
+ return (0);
+ dev = cndev;
+ }
+ if(cn_phys_close)
+ return ((*cn_phys_close)(dev, flag, mode, p));
+ return (0);
+}
+
+static int
+cnread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+
+ if (cn_tab == NULL || cn_phys_open == NULL)
+ return (0);
+ dev = cn_tab->cn_dev;
+ return ((*devsw(dev)->d_read)(dev, uio, flag));
+}
+
+static int
+cnwrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+
+ if (cn_tab == NULL || cn_phys_open == NULL) {
+ uio->uio_resid = 0; /* dump the data */
+ return (0);
+ }
+ if (constty)
+ dev = constty->t_dev;
+ else
+ dev = cn_tab->cn_dev;
+ return ((*devsw(dev)->d_write)(dev, uio, flag));
+}
+
+static int
+cnioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ int error;
+
+ if (cn_tab == NULL || cn_phys_open == NULL)
+ return (0);
+ /*
+ * Superuser can always use this to wrest control of console
+ * output from the "virtual" console.
+ */
+ if (cmd == TIOCCONS && constty) {
+ error = suser(p);
+ if (error)
+ return (error);
+ constty = NULL;
+ return (0);
+ }
+ dev = cn_tab->cn_dev;
+ return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, p));
+}
+
+static int
+cnpoll(dev, events, p)
+ dev_t dev;
+ int events;
+ struct proc *p;
+{
+ if ((cn_tab == NULL) || cn_mute)
+ return (1);
+
+ dev = cn_tab->cn_dev;
+
+ return ((*devsw(dev)->d_poll)(dev, events, p));
+}
+
+int
+cngetc()
+{
+ int c;
+ if ((cn_tab == NULL) || cn_mute)
+ return (-1);
+ c = (*cn_tab->cn_getc)(cn_tab->cn_dev);
+ if (c == '\r') c = '\n'; /* console input is always ICRNL */
+ return (c);
+}
+
+int
+cncheckc()
+{
+ if ((cn_tab == NULL) || cn_mute)
+ return (-1);
+ return ((*cn_tab->cn_checkc)(cn_tab->cn_dev));
+}
+
+void
+cnputc(c)
+ register int c;
+{
+ if ((cn_tab == NULL) || cn_mute)
+ return;
+ if (c) {
+ if (c == '\n')
+ (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
+ (*cn_tab->cn_putc)(cn_tab->cn_dev, c);
+ }
+}
+
+void
+cndbctl(on)
+ int on;
+{
+ static int refcount;
+
+ if (cn_tab == NULL)
+ return;
+ if (!on)
+ refcount--;
+ if (refcount == 0 && cn_tab->cn_dbctl != NULL)
+ (*cn_tab->cn_dbctl)(cn_tab->cn_dev, on);
+ if (on)
+ refcount++;
+}
+
+static void
+cn_drvinit(void *unused)
+{
+
+ cn_devfsdev = make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ "console");
+}
+
+SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)
OpenPOWER on IntegriCloud