The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_cons.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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  * 4. Neither the name of the University nor the names of its contributors
   19  *    may be used to endorse or promote products derived from this software
   20  *    without specific prior written permission.
   21  *
   22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   32  * SUCH DAMAGE.
   33  *
   34  *      from: @(#)cons.c        7.2 (Berkeley) 5/9/91
   35  */
   36 
   37 #include <sys/cdefs.h>
   38 __FBSDID("$FreeBSD: releng/8.2/sys/kern/kern_cons.c 199583 2009-11-20 15:27:52Z jhb $");
   39 
   40 #include "opt_ddb.h"
   41 
   42 #include <sys/param.h>
   43 #include <sys/systm.h>
   44 #include <sys/lock.h>
   45 #include <sys/mutex.h>
   46 #include <sys/conf.h>
   47 #include <sys/cons.h>
   48 #include <sys/fcntl.h>
   49 #include <sys/kdb.h>
   50 #include <sys/kernel.h>
   51 #include <sys/malloc.h>
   52 #include <sys/msgbuf.h>
   53 #include <sys/namei.h>
   54 #include <sys/priv.h>
   55 #include <sys/proc.h>
   56 #include <sys/queue.h>
   57 #include <sys/reboot.h>
   58 #include <sys/sysctl.h>
   59 #include <sys/sbuf.h>
   60 #include <sys/tty.h>
   61 #include <sys/uio.h>
   62 #include <sys/vnode.h>
   63 
   64 #include <ddb/ddb.h>
   65 
   66 #include <machine/cpu.h>
   67 #include <machine/clock.h>
   68 
   69 static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
   70 
   71 struct cn_device {
   72         STAILQ_ENTRY(cn_device) cnd_next;
   73         struct          consdev *cnd_cn;
   74 };
   75 
   76 #define CNDEVPATHMAX    32
   77 #define CNDEVTAB_SIZE   4
   78 static struct cn_device cn_devtab[CNDEVTAB_SIZE];
   79 static STAILQ_HEAD(, cn_device) cn_devlist =
   80     STAILQ_HEAD_INITIALIZER(cn_devlist);
   81 
   82 int     cons_avail_mask = 0;    /* Bit mask. Each registered low level console
   83                                  * which is currently unavailable for inpit
   84                                  * (i.e., if it is in graphics mode) will have
   85                                  * this bit cleared.
   86                                  */
   87 static int cn_mute;
   88 static char *consbuf;                   /* buffer used by `consmsgbuf' */
   89 static struct callout conscallout;      /* callout for outputting to constty */
   90 struct msgbuf consmsgbuf;               /* message buffer for console tty */
   91 static u_char console_pausing;          /* pause after each line during probe */
   92 static char *console_pausestr=
   93 "<pause; press any key to proceed to next line or '.' to end pause mode>";
   94 struct tty *constty;                    /* pointer to console "window" tty */
   95 static struct mtx cnputs_mtx;           /* Mutex for cnputs(). */
   96 static int use_cnputs_mtx = 0;          /* != 0 if cnputs_mtx locking reqd. */
   97 
   98 static void constty_timeout(void *arg);
   99 
  100 static struct consdev cons_consdev;
  101 DATA_SET(cons_set, cons_consdev);
  102 SET_DECLARE(cons_set, struct consdev);
  103 
  104 void
  105 cninit(void)
  106 {
  107         struct consdev *best_cn, *cn, **list;
  108 
  109         /*
  110          * Check if we should mute the console (for security reasons perhaps)
  111          * It can be changes dynamically using sysctl kern.consmute
  112          * once we are up and going.
  113          * 
  114          */
  115         cn_mute = ((boothowto & (RB_MUTE
  116                         |RB_SINGLE
  117                         |RB_VERBOSE
  118                         |RB_ASKNAME)) == RB_MUTE);
  119 
  120         /*
  121          * Find the first console with the highest priority.
  122          */
  123         best_cn = NULL;
  124         SET_FOREACH(list, cons_set) {
  125                 cn = *list;
  126                 cnremove(cn);
  127                 if (cn->cn_probe == NULL)
  128                         continue;
  129                 cn->cn_probe(cn);
  130                 if (cn->cn_pri == CN_DEAD)
  131                         continue;
  132                 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
  133                         best_cn = cn;
  134                 if (boothowto & RB_MULTIPLE) {
  135                         /*
  136                          * Initialize console, and attach to it.
  137                          */
  138                         cn->cn_init(cn);
  139                         cnadd(cn);
  140                 }
  141         }
  142         if (best_cn == NULL)
  143                 return;
  144         if ((boothowto & RB_MULTIPLE) == 0) {
  145                 best_cn->cn_init(best_cn);
  146                 cnadd(best_cn);
  147         }
  148         if (boothowto & RB_PAUSE)
  149                 console_pausing = 1;
  150         /*
  151          * Make the best console the preferred console.
  152          */
  153         cnselect(best_cn);
  154 }
  155 
  156 void
  157 cninit_finish()
  158 {
  159         console_pausing = 0;
  160 } 
  161 
  162 /* add a new physical console to back the virtual console */
  163 int
  164 cnadd(struct consdev *cn)
  165 {
  166         struct cn_device *cnd;
  167         int i;
  168 
  169         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
  170                 if (cnd->cnd_cn == cn)
  171                         return (0);
  172         for (i = 0; i < CNDEVTAB_SIZE; i++) {
  173                 cnd = &cn_devtab[i];
  174                 if (cnd->cnd_cn == NULL)
  175                         break;
  176         }
  177         if (cnd->cnd_cn != NULL)
  178                 return (ENOMEM);
  179         cnd->cnd_cn = cn;
  180         if (cn->cn_name[0] == '\0') {
  181                 /* XXX: it is unclear if/where this print might output */
  182                 printf("WARNING: console at %p has no name\n", cn);
  183         }
  184         STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
  185         if (STAILQ_FIRST(&cn_devlist) == cnd)
  186                 ttyconsdev_select(cnd->cnd_cn->cn_name);
  187 
  188         /* Add device to the active mask. */
  189         cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
  190 
  191         return (0);
  192 }
  193 
  194 void
  195 cnremove(struct consdev *cn)
  196 {
  197         struct cn_device *cnd;
  198         int i;
  199 
  200         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  201                 if (cnd->cnd_cn != cn)
  202                         continue;
  203                 if (STAILQ_FIRST(&cn_devlist) == cnd)
  204                         ttyconsdev_select(NULL);
  205                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
  206                 cnd->cnd_cn = NULL;
  207 
  208                 /* Remove this device from available mask. */
  209                 for (i = 0; i < CNDEVTAB_SIZE; i++) 
  210                         if (cnd == &cn_devtab[i]) {
  211                                 cons_avail_mask &= ~(1 << i);
  212                                 break;
  213                         }
  214 #if 0
  215                 /*
  216                  * XXX
  217                  * syscons gets really confused if console resources are
  218                  * freed after the system has initialized.
  219                  */
  220                 if (cn->cn_term != NULL)
  221                         cn->cn_term(cn);
  222 #endif
  223                 return;
  224         }
  225 }
  226 
  227 void
  228 cnselect(struct consdev *cn)
  229 {
  230         struct cn_device *cnd;
  231 
  232         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  233                 if (cnd->cnd_cn != cn)
  234                         continue;
  235                 if (cnd == STAILQ_FIRST(&cn_devlist))
  236                         return;
  237                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
  238                 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
  239                 ttyconsdev_select(cnd->cnd_cn->cn_name);
  240                 return;
  241         }
  242 }
  243 
  244 void
  245 cnavailable(struct consdev *cn, int available)
  246 {
  247         int i;
  248 
  249         for (i = 0; i < CNDEVTAB_SIZE; i++) {
  250                 if (cn_devtab[i].cnd_cn == cn)
  251                         break;
  252         }
  253         if (available) {
  254                 if (i < CNDEVTAB_SIZE)
  255                         cons_avail_mask |= (1 << i); 
  256                 cn->cn_flags &= ~CN_FLAG_NOAVAIL;
  257         } else {
  258                 if (i < CNDEVTAB_SIZE)
  259                         cons_avail_mask &= ~(1 << i);
  260                 cn->cn_flags |= CN_FLAG_NOAVAIL;
  261         }
  262 }
  263 
  264 int
  265 cnunavailable(void)
  266 {
  267 
  268         return (cons_avail_mask == 0);
  269 }
  270 
  271 /*
  272  * sysctl_kern_console() provides output parseable in conscontrol(1).
  273  */
  274 static int
  275 sysctl_kern_console(SYSCTL_HANDLER_ARGS)
  276 {
  277         struct cn_device *cnd;
  278         struct consdev *cp, **list;
  279         char *p;
  280         int delete, error;
  281         struct sbuf *sb;
  282 
  283         sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND);
  284         if (sb == NULL)
  285                 return (ENOMEM);
  286         sbuf_clear(sb);
  287         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
  288                 sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name);
  289         sbuf_printf(sb, "/");
  290         SET_FOREACH(list, cons_set) {
  291                 cp = *list;
  292                 if (cp->cn_name[0] != '\0')
  293                         sbuf_printf(sb, "%s,", cp->cn_name);
  294         }
  295         sbuf_finish(sb);
  296         error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
  297         if (error == 0 && req->newptr != NULL) {
  298                 p = sbuf_data(sb);
  299                 error = ENXIO;
  300                 delete = 0;
  301                 if (*p == '-') {
  302                         delete = 1;
  303                         p++;
  304                 }
  305                 SET_FOREACH(list, cons_set) {
  306                         cp = *list;
  307                         if (strcmp(p, cp->cn_name) != 0)
  308                                 continue;
  309                         if (delete) {
  310                                 cnremove(cp);
  311                                 error = 0;
  312                         } else {
  313                                 error = cnadd(cp);
  314                                 if (error == 0)
  315                                         cnselect(cp);
  316                         }
  317                         break;
  318                 }
  319         }
  320         sbuf_delete(sb);
  321         return (error);
  322 }
  323 
  324 SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
  325         0, 0, sysctl_kern_console, "A", "Console device control");
  326 
  327 /*
  328  * User has changed the state of the console muting.
  329  * This may require us to open or close the device in question.
  330  */
  331 static int
  332 sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
  333 {
  334         int error;
  335 
  336         error = sysctl_handle_int(oidp, &cn_mute, 0, req);
  337         if (error != 0 || req->newptr == NULL)
  338                 return (error);
  339         return (error);
  340 }
  341 
  342 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
  343         0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
  344 
  345 /*
  346  * Low level console routines.
  347  */
  348 int
  349 cngetc(void)
  350 {
  351         int c;
  352 
  353         if (cn_mute)
  354                 return (-1);
  355         while ((c = cncheckc()) == -1)
  356                 ;
  357         if (c == '\r')
  358                 c = '\n';               /* console input is always ICRNL */
  359         return (c);
  360 }
  361 
  362 int
  363 cncheckc(void)
  364 {
  365         struct cn_device *cnd;
  366         struct consdev *cn;
  367         int c;
  368 
  369         if (cn_mute)
  370                 return (-1);
  371         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  372                 cn = cnd->cnd_cn;
  373                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
  374                         if (cn->cn_checkc != NULL)
  375                                 c = cn->cn_checkc(cn);
  376                         else
  377                                 c = cn->cn_getc(cn);
  378                         if (c != -1) {
  379                                 return (c);
  380                         }
  381                 }
  382         }
  383         return (-1);
  384 }
  385 
  386 void
  387 cnputc(int c)
  388 {
  389         struct cn_device *cnd;
  390         struct consdev *cn;
  391         char *cp;
  392 
  393         if (cn_mute || c == '\0')
  394                 return;
  395         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  396                 cn = cnd->cnd_cn;
  397                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
  398                         if (c == '\n')
  399                                 cn->cn_putc(cn, '\r');
  400                         cn->cn_putc(cn, c);
  401                 }
  402         }
  403         if (console_pausing && c == '\n' && !kdb_active) {
  404                 for (cp = console_pausestr; *cp != '\0'; cp++)
  405                         cnputc(*cp);
  406                 if (cngetc() == '.')
  407                         console_pausing = 0;
  408                 cnputc('\r');
  409                 for (cp = console_pausestr; *cp != '\0'; cp++)
  410                         cnputc(' ');
  411                 cnputc('\r');
  412         }
  413 }
  414 
  415 void
  416 cnputs(char *p)
  417 {
  418         int c;
  419         int unlock_reqd = 0;
  420 
  421         if (use_cnputs_mtx) {
  422                 mtx_lock_spin(&cnputs_mtx);
  423                 unlock_reqd = 1;
  424         }
  425 
  426         while ((c = *p++) != '\0')
  427                 cnputc(c);
  428 
  429         if (unlock_reqd)
  430                 mtx_unlock_spin(&cnputs_mtx);
  431 }
  432 
  433 static int consmsgbuf_size = 8192;
  434 SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
  435     "");
  436 
  437 /*
  438  * Redirect console output to a tty.
  439  */
  440 void
  441 constty_set(struct tty *tp)
  442 {
  443         int size;
  444 
  445         KASSERT(tp != NULL, ("constty_set: NULL tp"));
  446         if (consbuf == NULL) {
  447                 size = consmsgbuf_size;
  448                 consbuf = malloc(size, M_TTYCONS, M_WAITOK);
  449                 msgbuf_init(&consmsgbuf, consbuf, size);
  450                 callout_init(&conscallout, 0);
  451         }
  452         constty = tp;
  453         constty_timeout(NULL);
  454 }
  455 
  456 /*
  457  * Disable console redirection to a tty.
  458  */
  459 void
  460 constty_clear(void)
  461 {
  462         int c;
  463 
  464         constty = NULL;
  465         if (consbuf == NULL)
  466                 return;
  467         callout_stop(&conscallout);
  468         while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
  469                 cnputc(c);
  470         free(consbuf, M_TTYCONS);
  471         consbuf = NULL;
  472 }
  473 
  474 /* Times per second to check for pending console tty messages. */
  475 static int constty_wakeups_per_second = 5;
  476 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
  477     &constty_wakeups_per_second, 0, "");
  478 
  479 static void
  480 constty_timeout(void *arg)
  481 {
  482         int c;
  483 
  484         if (constty != NULL) {
  485                 tty_lock(constty);
  486                 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
  487                         if (tty_putchar(constty, c) < 0) {
  488                                 tty_unlock(constty);
  489                                 constty = NULL;
  490                                 break;
  491                         }
  492                 }
  493 
  494                 if (constty != NULL)
  495                         tty_unlock(constty);
  496         }
  497         if (constty != NULL) {
  498                 callout_reset(&conscallout, hz / constty_wakeups_per_second,
  499                     constty_timeout, NULL);
  500         } else {
  501                 /* Deallocate the constty buffer memory. */
  502                 constty_clear();
  503         }
  504 }
  505 
  506 static void
  507 cn_drvinit(void *unused)
  508 {
  509 
  510         mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
  511         use_cnputs_mtx = 1;
  512 }
  513 
  514 SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
  515 
  516 /*
  517  * Sysbeep(), if we have hardware for it
  518  */
  519 
  520 #ifdef HAS_TIMER_SPKR
  521 
  522 static int beeping;
  523 
  524 static void
  525 sysbeepstop(void *chan)
  526 {
  527 
  528         timer_spkr_release();
  529         beeping = 0;
  530 }
  531 
  532 int
  533 sysbeep(int pitch, int period)
  534 {
  535 
  536         if (timer_spkr_acquire()) {
  537                 if (!beeping) {
  538                         /* Something else owns it. */
  539                         return (EBUSY);
  540                 }
  541         }
  542         timer_spkr_setfreq(pitch);
  543         if (!beeping) {
  544                 beeping = period;
  545                 timeout(sysbeepstop, (void *)NULL, period);
  546         }
  547         return (0);
  548 }
  549 
  550 #else
  551 
  552 /*
  553  * No hardware, no sound
  554  */
  555 
  556 int
  557 sysbeep(int pitch __unused, int period __unused)
  558 {
  559 
  560         return (ENODEV);
  561 }
  562 
  563 #endif
  564 

Cache object: a2603ce19998edb9daefe0ce5a18a778


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.