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