1c9dba40cSEd Schouten /*- 2c9dba40cSEd Schouten * Copyright (c) 1988 University of Utah. 3c9dba40cSEd Schouten * Copyright (c) 1991 The Regents of the University of California. 4f389bc95SAndriy Gapon * Copyright (c) 1999 Michael Smith 5f389bc95SAndriy Gapon * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 6f389bc95SAndriy Gapon * 7c9dba40cSEd Schouten * All rights reserved. 8c9dba40cSEd Schouten * 9c9dba40cSEd Schouten * This code is derived from software contributed to Berkeley by 10c9dba40cSEd Schouten * the Systems Programming Group of the University of Utah Computer 11c9dba40cSEd Schouten * Science Department. 12c9dba40cSEd Schouten * 13c9dba40cSEd Schouten * Redistribution and use in source and binary forms, with or without 14c9dba40cSEd Schouten * modification, are permitted provided that the following conditions 15c9dba40cSEd Schouten * are met: 16c9dba40cSEd Schouten * 1. Redistributions of source code must retain the above copyright 17c9dba40cSEd Schouten * notice, this list of conditions and the following disclaimer. 18c9dba40cSEd Schouten * 2. Redistributions in binary form must reproduce the above copyright 19c9dba40cSEd Schouten * notice, this list of conditions and the following disclaimer in the 20c9dba40cSEd Schouten * documentation and/or other materials provided with the distribution. 21c9dba40cSEd Schouten * 4. Neither the name of the University nor the names of its contributors 22c9dba40cSEd Schouten * may be used to endorse or promote products derived from this software 23c9dba40cSEd Schouten * without specific prior written permission. 24c9dba40cSEd Schouten * 25c9dba40cSEd Schouten * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26c9dba40cSEd Schouten * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27c9dba40cSEd Schouten * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28c9dba40cSEd Schouten * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29c9dba40cSEd Schouten * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30c9dba40cSEd Schouten * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31c9dba40cSEd Schouten * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32c9dba40cSEd Schouten * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33c9dba40cSEd Schouten * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34c9dba40cSEd Schouten * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35c9dba40cSEd Schouten * SUCH DAMAGE. 36c9dba40cSEd Schouten * 37c9dba40cSEd Schouten * from: @(#)cons.c 7.2 (Berkeley) 5/9/91 38c9dba40cSEd Schouten */ 39c9dba40cSEd Schouten 40c9dba40cSEd Schouten #include <sys/cdefs.h> 41c9dba40cSEd Schouten __FBSDID("$FreeBSD$"); 42c9dba40cSEd Schouten 43c9dba40cSEd Schouten #include "opt_ddb.h" 4459644098SEd Maste #include "opt_syscons.h" 45c9dba40cSEd Schouten 46c9dba40cSEd Schouten #include <sys/param.h> 47c9dba40cSEd Schouten #include <sys/systm.h> 48c9dba40cSEd Schouten #include <sys/lock.h> 49c9dba40cSEd Schouten #include <sys/mutex.h> 50c9dba40cSEd Schouten #include <sys/conf.h> 51c9dba40cSEd Schouten #include <sys/cons.h> 52c9dba40cSEd Schouten #include <sys/fcntl.h> 53c9dba40cSEd Schouten #include <sys/kdb.h> 54c9dba40cSEd Schouten #include <sys/kernel.h> 55c9dba40cSEd Schouten #include <sys/malloc.h> 56c9dba40cSEd Schouten #include <sys/msgbuf.h> 57c9dba40cSEd Schouten #include <sys/namei.h> 58c9dba40cSEd Schouten #include <sys/priv.h> 59c9dba40cSEd Schouten #include <sys/proc.h> 60c9dba40cSEd Schouten #include <sys/queue.h> 61c9dba40cSEd Schouten #include <sys/reboot.h> 62c9dba40cSEd Schouten #include <sys/sysctl.h> 63c9dba40cSEd Schouten #include <sys/sbuf.h> 64c9dba40cSEd Schouten #include <sys/tty.h> 65c9dba40cSEd Schouten #include <sys/uio.h> 66c9dba40cSEd Schouten #include <sys/vnode.h> 67c9dba40cSEd Schouten 68c9dba40cSEd Schouten #include <ddb/ddb.h> 69c9dba40cSEd Schouten 70c9dba40cSEd Schouten #include <machine/cpu.h> 71c9dba40cSEd Schouten #include <machine/clock.h> 72c9dba40cSEd Schouten 73c9dba40cSEd Schouten static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling"); 74c9dba40cSEd Schouten 75c9dba40cSEd Schouten struct cn_device { 76c9dba40cSEd Schouten STAILQ_ENTRY(cn_device) cnd_next; 77c9dba40cSEd Schouten struct consdev *cnd_cn; 78c9dba40cSEd Schouten }; 79c9dba40cSEd Schouten 80c9dba40cSEd Schouten #define CNDEVPATHMAX 32 81c9dba40cSEd Schouten #define CNDEVTAB_SIZE 4 82c9dba40cSEd Schouten static struct cn_device cn_devtab[CNDEVTAB_SIZE]; 83c9dba40cSEd Schouten static STAILQ_HEAD(, cn_device) cn_devlist = 84c9dba40cSEd Schouten STAILQ_HEAD_INITIALIZER(cn_devlist); 85c9dba40cSEd Schouten 86c9dba40cSEd Schouten int cons_avail_mask = 0; /* Bit mask. Each registered low level console 87c9dba40cSEd Schouten * which is currently unavailable for inpit 88c9dba40cSEd Schouten * (i.e., if it is in graphics mode) will have 89c9dba40cSEd Schouten * this bit cleared. 90c9dba40cSEd Schouten */ 91c9dba40cSEd Schouten static int cn_mute; 92c9dba40cSEd Schouten static char *consbuf; /* buffer used by `consmsgbuf' */ 93c9dba40cSEd Schouten static struct callout conscallout; /* callout for outputting to constty */ 94c9dba40cSEd Schouten struct msgbuf consmsgbuf; /* message buffer for console tty */ 95c9dba40cSEd Schouten static u_char console_pausing; /* pause after each line during probe */ 96c9dba40cSEd Schouten static char *console_pausestr= 97c9dba40cSEd Schouten "<pause; press any key to proceed to next line or '.' to end pause mode>"; 98c9dba40cSEd Schouten struct tty *constty; /* pointer to console "window" tty */ 99c9dba40cSEd Schouten static struct mtx cnputs_mtx; /* Mutex for cnputs(). */ 100c9dba40cSEd Schouten static int use_cnputs_mtx = 0; /* != 0 if cnputs_mtx locking reqd. */ 101c9dba40cSEd Schouten 102c9dba40cSEd Schouten static void constty_timeout(void *arg); 103c9dba40cSEd Schouten 104c9dba40cSEd Schouten static struct consdev cons_consdev; 105c9dba40cSEd Schouten DATA_SET(cons_set, cons_consdev); 106c9dba40cSEd Schouten SET_DECLARE(cons_set, struct consdev); 107c9dba40cSEd Schouten 108c9dba40cSEd Schouten void 109c9dba40cSEd Schouten cninit(void) 110c9dba40cSEd Schouten { 111c9dba40cSEd Schouten struct consdev *best_cn, *cn, **list; 112c9dba40cSEd Schouten 113c9dba40cSEd Schouten /* 114c9dba40cSEd Schouten * Check if we should mute the console (for security reasons perhaps) 115c9dba40cSEd Schouten * It can be changes dynamically using sysctl kern.consmute 116c9dba40cSEd Schouten * once we are up and going. 117c9dba40cSEd Schouten * 118c9dba40cSEd Schouten */ 119c9dba40cSEd Schouten cn_mute = ((boothowto & (RB_MUTE 120c9dba40cSEd Schouten |RB_SINGLE 121c9dba40cSEd Schouten |RB_VERBOSE 122c9dba40cSEd Schouten |RB_ASKNAME)) == RB_MUTE); 123c9dba40cSEd Schouten 124c9dba40cSEd Schouten /* 125c9dba40cSEd Schouten * Find the first console with the highest priority. 126c9dba40cSEd Schouten */ 127c9dba40cSEd Schouten best_cn = NULL; 128c9dba40cSEd Schouten SET_FOREACH(list, cons_set) { 129c9dba40cSEd Schouten cn = *list; 130c9dba40cSEd Schouten cnremove(cn); 1312992abe0SEd Schouten /* Skip cons_consdev. */ 1322992abe0SEd Schouten if (cn->cn_ops == NULL) 133c9dba40cSEd Schouten continue; 1342992abe0SEd Schouten cn->cn_ops->cn_probe(cn); 135c9dba40cSEd Schouten if (cn->cn_pri == CN_DEAD) 136c9dba40cSEd Schouten continue; 137c9dba40cSEd Schouten if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) 138c9dba40cSEd Schouten best_cn = cn; 139c9dba40cSEd Schouten if (boothowto & RB_MULTIPLE) { 140c9dba40cSEd Schouten /* 141c9dba40cSEd Schouten * Initialize console, and attach to it. 142c9dba40cSEd Schouten */ 1432992abe0SEd Schouten cn->cn_ops->cn_init(cn); 144c9dba40cSEd Schouten cnadd(cn); 145c9dba40cSEd Schouten } 146c9dba40cSEd Schouten } 147c9dba40cSEd Schouten if (best_cn == NULL) 148c9dba40cSEd Schouten return; 149c9dba40cSEd Schouten if ((boothowto & RB_MULTIPLE) == 0) { 1502992abe0SEd Schouten best_cn->cn_ops->cn_init(best_cn); 151c9dba40cSEd Schouten cnadd(best_cn); 152c9dba40cSEd Schouten } 153c9dba40cSEd Schouten if (boothowto & RB_PAUSE) 154c9dba40cSEd Schouten console_pausing = 1; 155c9dba40cSEd Schouten /* 156c9dba40cSEd Schouten * Make the best console the preferred console. 157c9dba40cSEd Schouten */ 158c9dba40cSEd Schouten cnselect(best_cn); 159dc61566fSZbigniew Bodek 160dc61566fSZbigniew Bodek #ifdef EARLY_PRINTF 161dc61566fSZbigniew Bodek /* 162dc61566fSZbigniew Bodek * Release early console. 163dc61566fSZbigniew Bodek */ 164dc61566fSZbigniew Bodek early_putc = NULL; 165dc61566fSZbigniew Bodek #endif 166c9dba40cSEd Schouten } 167c9dba40cSEd Schouten 168c9dba40cSEd Schouten void 169c9dba40cSEd Schouten cninit_finish() 170c9dba40cSEd Schouten { 171c9dba40cSEd Schouten console_pausing = 0; 172c9dba40cSEd Schouten } 173c9dba40cSEd Schouten 174c9dba40cSEd Schouten /* add a new physical console to back the virtual console */ 175c9dba40cSEd Schouten int 176c9dba40cSEd Schouten cnadd(struct consdev *cn) 177c9dba40cSEd Schouten { 178c9dba40cSEd Schouten struct cn_device *cnd; 179c9dba40cSEd Schouten int i; 180c9dba40cSEd Schouten 181c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 182c9dba40cSEd Schouten if (cnd->cnd_cn == cn) 183c9dba40cSEd Schouten return (0); 184c9dba40cSEd Schouten for (i = 0; i < CNDEVTAB_SIZE; i++) { 185c9dba40cSEd Schouten cnd = &cn_devtab[i]; 186c9dba40cSEd Schouten if (cnd->cnd_cn == NULL) 187c9dba40cSEd Schouten break; 188c9dba40cSEd Schouten } 189c9dba40cSEd Schouten if (cnd->cnd_cn != NULL) 190c9dba40cSEd Schouten return (ENOMEM); 191c9dba40cSEd Schouten cnd->cnd_cn = cn; 192c9dba40cSEd Schouten if (cn->cn_name[0] == '\0') { 193c9dba40cSEd Schouten /* XXX: it is unclear if/where this print might output */ 194c9dba40cSEd Schouten printf("WARNING: console at %p has no name\n", cn); 195c9dba40cSEd Schouten } 196c9dba40cSEd Schouten STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); 197c9dba40cSEd Schouten if (STAILQ_FIRST(&cn_devlist) == cnd) 198c9dba40cSEd Schouten ttyconsdev_select(cnd->cnd_cn->cn_name); 199c9dba40cSEd Schouten 200c9dba40cSEd Schouten /* Add device to the active mask. */ 201c9dba40cSEd Schouten cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0); 202c9dba40cSEd Schouten 203c9dba40cSEd Schouten return (0); 204c9dba40cSEd Schouten } 205c9dba40cSEd Schouten 206c9dba40cSEd Schouten void 207c9dba40cSEd Schouten cnremove(struct consdev *cn) 208c9dba40cSEd Schouten { 209c9dba40cSEd Schouten struct cn_device *cnd; 210c9dba40cSEd Schouten int i; 211c9dba40cSEd Schouten 212c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 213c9dba40cSEd Schouten if (cnd->cnd_cn != cn) 214c9dba40cSEd Schouten continue; 215c9dba40cSEd Schouten if (STAILQ_FIRST(&cn_devlist) == cnd) 216c9dba40cSEd Schouten ttyconsdev_select(NULL); 217c9dba40cSEd Schouten STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 218c9dba40cSEd Schouten cnd->cnd_cn = NULL; 219c9dba40cSEd Schouten 220c9dba40cSEd Schouten /* Remove this device from available mask. */ 221c9dba40cSEd Schouten for (i = 0; i < CNDEVTAB_SIZE; i++) 222c9dba40cSEd Schouten if (cnd == &cn_devtab[i]) { 223c9dba40cSEd Schouten cons_avail_mask &= ~(1 << i); 224c9dba40cSEd Schouten break; 225c9dba40cSEd Schouten } 226c9dba40cSEd Schouten #if 0 227c9dba40cSEd Schouten /* 228c9dba40cSEd Schouten * XXX 229c9dba40cSEd Schouten * syscons gets really confused if console resources are 230c9dba40cSEd Schouten * freed after the system has initialized. 231c9dba40cSEd Schouten */ 232c9dba40cSEd Schouten if (cn->cn_term != NULL) 2332992abe0SEd Schouten cn->cn_ops->cn_term(cn); 234c9dba40cSEd Schouten #endif 235c9dba40cSEd Schouten return; 236c9dba40cSEd Schouten } 237c9dba40cSEd Schouten } 238c9dba40cSEd Schouten 239c9dba40cSEd Schouten void 240c9dba40cSEd Schouten cnselect(struct consdev *cn) 241c9dba40cSEd Schouten { 242c9dba40cSEd Schouten struct cn_device *cnd; 243c9dba40cSEd Schouten 244c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 245c9dba40cSEd Schouten if (cnd->cnd_cn != cn) 246c9dba40cSEd Schouten continue; 247c9dba40cSEd Schouten if (cnd == STAILQ_FIRST(&cn_devlist)) 248c9dba40cSEd Schouten return; 249c9dba40cSEd Schouten STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 250c9dba40cSEd Schouten STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); 251c9dba40cSEd Schouten ttyconsdev_select(cnd->cnd_cn->cn_name); 252c9dba40cSEd Schouten return; 253c9dba40cSEd Schouten } 254c9dba40cSEd Schouten } 255c9dba40cSEd Schouten 256c9dba40cSEd Schouten void 257c9dba40cSEd Schouten cnavailable(struct consdev *cn, int available) 258c9dba40cSEd Schouten { 259c9dba40cSEd Schouten int i; 260c9dba40cSEd Schouten 261c9dba40cSEd Schouten for (i = 0; i < CNDEVTAB_SIZE; i++) { 262c9dba40cSEd Schouten if (cn_devtab[i].cnd_cn == cn) 263c9dba40cSEd Schouten break; 264c9dba40cSEd Schouten } 265c9dba40cSEd Schouten if (available) { 266c9dba40cSEd Schouten if (i < CNDEVTAB_SIZE) 267c9dba40cSEd Schouten cons_avail_mask |= (1 << i); 268c9dba40cSEd Schouten cn->cn_flags &= ~CN_FLAG_NOAVAIL; 269c9dba40cSEd Schouten } else { 270c9dba40cSEd Schouten if (i < CNDEVTAB_SIZE) 271c9dba40cSEd Schouten cons_avail_mask &= ~(1 << i); 272c9dba40cSEd Schouten cn->cn_flags |= CN_FLAG_NOAVAIL; 273c9dba40cSEd Schouten } 274c9dba40cSEd Schouten } 275c9dba40cSEd Schouten 276c9dba40cSEd Schouten int 277c9dba40cSEd Schouten cnunavailable(void) 278c9dba40cSEd Schouten { 279c9dba40cSEd Schouten 280c9dba40cSEd Schouten return (cons_avail_mask == 0); 281c9dba40cSEd Schouten } 282c9dba40cSEd Schouten 283c9dba40cSEd Schouten /* 284c9dba40cSEd Schouten * sysctl_kern_console() provides output parseable in conscontrol(1). 285c9dba40cSEd Schouten */ 286c9dba40cSEd Schouten static int 287c9dba40cSEd Schouten sysctl_kern_console(SYSCTL_HANDLER_ARGS) 288c9dba40cSEd Schouten { 289c9dba40cSEd Schouten struct cn_device *cnd; 290c9dba40cSEd Schouten struct consdev *cp, **list; 291c9dba40cSEd Schouten char *p; 292c9dba40cSEd Schouten int delete, error; 293c9dba40cSEd Schouten struct sbuf *sb; 294c9dba40cSEd Schouten 295*657282e0SIan Lepore sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND | 296*657282e0SIan Lepore SBUF_INCLUDENUL); 297c9dba40cSEd Schouten if (sb == NULL) 298c9dba40cSEd Schouten return (ENOMEM); 299c9dba40cSEd Schouten sbuf_clear(sb); 300c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 301c9dba40cSEd Schouten sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name); 302c9dba40cSEd Schouten sbuf_printf(sb, "/"); 303c9dba40cSEd Schouten SET_FOREACH(list, cons_set) { 304c9dba40cSEd Schouten cp = *list; 305c9dba40cSEd Schouten if (cp->cn_name[0] != '\0') 306c9dba40cSEd Schouten sbuf_printf(sb, "%s,", cp->cn_name); 307c9dba40cSEd Schouten } 308c9dba40cSEd Schouten sbuf_finish(sb); 309c9dba40cSEd Schouten error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req); 310c9dba40cSEd Schouten if (error == 0 && req->newptr != NULL) { 311c9dba40cSEd Schouten p = sbuf_data(sb); 312c9dba40cSEd Schouten error = ENXIO; 313c9dba40cSEd Schouten delete = 0; 314c9dba40cSEd Schouten if (*p == '-') { 315c9dba40cSEd Schouten delete = 1; 316c9dba40cSEd Schouten p++; 317c9dba40cSEd Schouten } 318c9dba40cSEd Schouten SET_FOREACH(list, cons_set) { 319c9dba40cSEd Schouten cp = *list; 320c9dba40cSEd Schouten if (strcmp(p, cp->cn_name) != 0) 321c9dba40cSEd Schouten continue; 322c9dba40cSEd Schouten if (delete) { 323c9dba40cSEd Schouten cnremove(cp); 324c9dba40cSEd Schouten error = 0; 325c9dba40cSEd Schouten } else { 326c9dba40cSEd Schouten error = cnadd(cp); 327c9dba40cSEd Schouten if (error == 0) 328c9dba40cSEd Schouten cnselect(cp); 329c9dba40cSEd Schouten } 330c9dba40cSEd Schouten break; 331c9dba40cSEd Schouten } 332c9dba40cSEd Schouten } 333c9dba40cSEd Schouten sbuf_delete(sb); 334c9dba40cSEd Schouten return (error); 335c9dba40cSEd Schouten } 336c9dba40cSEd Schouten 337c9dba40cSEd Schouten SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW, 338c9dba40cSEd Schouten 0, 0, sysctl_kern_console, "A", "Console device control"); 339c9dba40cSEd Schouten 340c9dba40cSEd Schouten /* 341c9dba40cSEd Schouten * User has changed the state of the console muting. 342c9dba40cSEd Schouten * This may require us to open or close the device in question. 343c9dba40cSEd Schouten */ 344c9dba40cSEd Schouten static int 345c9dba40cSEd Schouten sysctl_kern_consmute(SYSCTL_HANDLER_ARGS) 346c9dba40cSEd Schouten { 347c9dba40cSEd Schouten int error; 348c9dba40cSEd Schouten 349c9dba40cSEd Schouten error = sysctl_handle_int(oidp, &cn_mute, 0, req); 350c9dba40cSEd Schouten if (error != 0 || req->newptr == NULL) 351c9dba40cSEd Schouten return (error); 352c9dba40cSEd Schouten return (error); 353c9dba40cSEd Schouten } 354c9dba40cSEd Schouten 355c9dba40cSEd Schouten SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW, 356a0c87b74SGavin Atkinson 0, sizeof(cn_mute), sysctl_kern_consmute, "I", 357a0c87b74SGavin Atkinson "State of the console muting"); 358c9dba40cSEd Schouten 3599976156fSAndriy Gapon void 3609976156fSAndriy Gapon cngrab() 3619976156fSAndriy Gapon { 3629976156fSAndriy Gapon struct cn_device *cnd; 3639976156fSAndriy Gapon struct consdev *cn; 3649976156fSAndriy Gapon 3659976156fSAndriy Gapon STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 3669976156fSAndriy Gapon cn = cnd->cnd_cn; 3679976156fSAndriy Gapon if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) 3689976156fSAndriy Gapon cn->cn_ops->cn_grab(cn); 3699976156fSAndriy Gapon } 3709976156fSAndriy Gapon } 3719976156fSAndriy Gapon 3729976156fSAndriy Gapon void 3739976156fSAndriy Gapon cnungrab() 3749976156fSAndriy Gapon { 3759976156fSAndriy Gapon struct cn_device *cnd; 3769976156fSAndriy Gapon struct consdev *cn; 3779976156fSAndriy Gapon 3789976156fSAndriy Gapon STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 3799976156fSAndriy Gapon cn = cnd->cnd_cn; 3809976156fSAndriy Gapon if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) 3819976156fSAndriy Gapon cn->cn_ops->cn_ungrab(cn); 3829976156fSAndriy Gapon } 3839976156fSAndriy Gapon } 3849976156fSAndriy Gapon 385c9dba40cSEd Schouten /* 386c9dba40cSEd Schouten * Low level console routines. 387c9dba40cSEd Schouten */ 388c9dba40cSEd Schouten int 389c9dba40cSEd Schouten cngetc(void) 390c9dba40cSEd Schouten { 391c9dba40cSEd Schouten int c; 392c9dba40cSEd Schouten 393c9dba40cSEd Schouten if (cn_mute) 394c9dba40cSEd Schouten return (-1); 395c9dba40cSEd Schouten while ((c = cncheckc()) == -1) 396298fbd16SAndriy Gapon cpu_spinwait(); 397c9dba40cSEd Schouten if (c == '\r') 398c9dba40cSEd Schouten c = '\n'; /* console input is always ICRNL */ 399c9dba40cSEd Schouten return (c); 400c9dba40cSEd Schouten } 401c9dba40cSEd Schouten 402c9dba40cSEd Schouten int 403c9dba40cSEd Schouten cncheckc(void) 404c9dba40cSEd Schouten { 405c9dba40cSEd Schouten struct cn_device *cnd; 406c9dba40cSEd Schouten struct consdev *cn; 407c9dba40cSEd Schouten int c; 408c9dba40cSEd Schouten 409c9dba40cSEd Schouten if (cn_mute) 410c9dba40cSEd Schouten return (-1); 411c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 412c9dba40cSEd Schouten cn = cnd->cnd_cn; 413c9dba40cSEd Schouten if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 4142992abe0SEd Schouten c = cn->cn_ops->cn_getc(cn); 4152992abe0SEd Schouten if (c != -1) 416c9dba40cSEd Schouten return (c); 417c9dba40cSEd Schouten } 418c9dba40cSEd Schouten } 419c9dba40cSEd Schouten return (-1); 420c9dba40cSEd Schouten } 421c9dba40cSEd Schouten 422c9dba40cSEd Schouten void 4238e628542SAndriy Gapon cngets(char *cp, size_t size, int visible) 4248e628542SAndriy Gapon { 4258e628542SAndriy Gapon char *lp, *end; 4268e628542SAndriy Gapon int c; 4278e628542SAndriy Gapon 4288e628542SAndriy Gapon cngrab(); 4298e628542SAndriy Gapon 4308e628542SAndriy Gapon lp = cp; 4318e628542SAndriy Gapon end = cp + size - 1; 4328e628542SAndriy Gapon for (;;) { 4338e628542SAndriy Gapon c = cngetc() & 0177; 4348e628542SAndriy Gapon switch (c) { 4358e628542SAndriy Gapon case '\n': 4368e628542SAndriy Gapon case '\r': 4378e628542SAndriy Gapon cnputc(c); 4388e628542SAndriy Gapon *lp = '\0'; 4398e628542SAndriy Gapon cnungrab(); 4408e628542SAndriy Gapon return; 4418e628542SAndriy Gapon case '\b': 4428e628542SAndriy Gapon case '\177': 4438e628542SAndriy Gapon if (lp > cp) { 4449520f952SWarner Losh if (visible) 4459520f952SWarner Losh cnputs("\b \b"); 4468e628542SAndriy Gapon lp--; 4478e628542SAndriy Gapon } 4488e628542SAndriy Gapon continue; 4498e628542SAndriy Gapon case '\0': 4508e628542SAndriy Gapon continue; 4518e628542SAndriy Gapon default: 4528e628542SAndriy Gapon if (lp < end) { 4538e628542SAndriy Gapon switch (visible) { 4548e628542SAndriy Gapon case GETS_NOECHO: 4558e628542SAndriy Gapon break; 4568e628542SAndriy Gapon case GETS_ECHOPASS: 4578e628542SAndriy Gapon cnputc('*'); 4588e628542SAndriy Gapon break; 4598e628542SAndriy Gapon default: 4608e628542SAndriy Gapon cnputc(c); 4618e628542SAndriy Gapon break; 4628e628542SAndriy Gapon } 4638e628542SAndriy Gapon *lp++ = c; 4648e628542SAndriy Gapon } 4658e628542SAndriy Gapon } 4668e628542SAndriy Gapon } 4678e628542SAndriy Gapon } 4688e628542SAndriy Gapon 4698e628542SAndriy Gapon void 470c9dba40cSEd Schouten cnputc(int c) 471c9dba40cSEd Schouten { 472c9dba40cSEd Schouten struct cn_device *cnd; 473c9dba40cSEd Schouten struct consdev *cn; 474c9dba40cSEd Schouten char *cp; 475c9dba40cSEd Schouten 47642c8459bSIan Lepore #ifdef EARLY_PRINTF 47742c8459bSIan Lepore if (early_putc != NULL) { 47842c8459bSIan Lepore if (c == '\n') 47942c8459bSIan Lepore early_putc('\r'); 48042c8459bSIan Lepore early_putc(c); 48142c8459bSIan Lepore return; 48242c8459bSIan Lepore } 48342c8459bSIan Lepore #endif 48442c8459bSIan Lepore 485c9dba40cSEd Schouten if (cn_mute || c == '\0') 486c9dba40cSEd Schouten return; 487c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 488c9dba40cSEd Schouten cn = cnd->cnd_cn; 489c9dba40cSEd Schouten if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 490c9dba40cSEd Schouten if (c == '\n') 4912992abe0SEd Schouten cn->cn_ops->cn_putc(cn, '\r'); 4922992abe0SEd Schouten cn->cn_ops->cn_putc(cn, c); 493c9dba40cSEd Schouten } 494c9dba40cSEd Schouten } 495c9dba40cSEd Schouten if (console_pausing && c == '\n' && !kdb_active) { 496c9dba40cSEd Schouten for (cp = console_pausestr; *cp != '\0'; cp++) 497c9dba40cSEd Schouten cnputc(*cp); 498bf8696b4SAndriy Gapon cngrab(); 499c9dba40cSEd Schouten if (cngetc() == '.') 500c9dba40cSEd Schouten console_pausing = 0; 501bf8696b4SAndriy Gapon cnungrab(); 502c9dba40cSEd Schouten cnputc('\r'); 503c9dba40cSEd Schouten for (cp = console_pausestr; *cp != '\0'; cp++) 504c9dba40cSEd Schouten cnputc(' '); 505c9dba40cSEd Schouten cnputc('\r'); 506c9dba40cSEd Schouten } 507c9dba40cSEd Schouten } 508c9dba40cSEd Schouten 509c9dba40cSEd Schouten void 510c9dba40cSEd Schouten cnputs(char *p) 511c9dba40cSEd Schouten { 512c9dba40cSEd Schouten int c; 513c9dba40cSEd Schouten int unlock_reqd = 0; 514c9dba40cSEd Schouten 515c9dba40cSEd Schouten if (use_cnputs_mtx) { 51604a8159dSHans Petter Selasky /* 51704a8159dSHans Petter Selasky * NOTE: Debug prints and/or witness printouts in 51804a8159dSHans Petter Selasky * console driver clients can cause the "cnputs_mtx" 51904a8159dSHans Petter Selasky * mutex to recurse. Simply return if that happens. 52004a8159dSHans Petter Selasky */ 52104a8159dSHans Petter Selasky if (mtx_owned(&cnputs_mtx)) 52204a8159dSHans Petter Selasky return; 523c9dba40cSEd Schouten mtx_lock_spin(&cnputs_mtx); 524c9dba40cSEd Schouten unlock_reqd = 1; 525c9dba40cSEd Schouten } 526c9dba40cSEd Schouten 527c9dba40cSEd Schouten while ((c = *p++) != '\0') 528c9dba40cSEd Schouten cnputc(c); 529c9dba40cSEd Schouten 530c9dba40cSEd Schouten if (unlock_reqd) 531c9dba40cSEd Schouten mtx_unlock_spin(&cnputs_mtx); 532c9dba40cSEd Schouten } 533c9dba40cSEd Schouten 534c9dba40cSEd Schouten static int consmsgbuf_size = 8192; 535c9dba40cSEd Schouten SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0, 536a0c87b74SGavin Atkinson "Console tty buffer size"); 537c9dba40cSEd Schouten 538c9dba40cSEd Schouten /* 539c9dba40cSEd Schouten * Redirect console output to a tty. 540c9dba40cSEd Schouten */ 541c9dba40cSEd Schouten void 542c9dba40cSEd Schouten constty_set(struct tty *tp) 543c9dba40cSEd Schouten { 544c9dba40cSEd Schouten int size; 545c9dba40cSEd Schouten 546c9dba40cSEd Schouten KASSERT(tp != NULL, ("constty_set: NULL tp")); 547c9dba40cSEd Schouten if (consbuf == NULL) { 548c9dba40cSEd Schouten size = consmsgbuf_size; 549c9dba40cSEd Schouten consbuf = malloc(size, M_TTYCONS, M_WAITOK); 550c9dba40cSEd Schouten msgbuf_init(&consmsgbuf, consbuf, size); 551c9dba40cSEd Schouten callout_init(&conscallout, 0); 552c9dba40cSEd Schouten } 553c9dba40cSEd Schouten constty = tp; 554c9dba40cSEd Schouten constty_timeout(NULL); 555c9dba40cSEd Schouten } 556c9dba40cSEd Schouten 557c9dba40cSEd Schouten /* 558c9dba40cSEd Schouten * Disable console redirection to a tty. 559c9dba40cSEd Schouten */ 560c9dba40cSEd Schouten void 561c9dba40cSEd Schouten constty_clear(void) 562c9dba40cSEd Schouten { 563c9dba40cSEd Schouten int c; 564c9dba40cSEd Schouten 565c9dba40cSEd Schouten constty = NULL; 566c9dba40cSEd Schouten if (consbuf == NULL) 567c9dba40cSEd Schouten return; 568c9dba40cSEd Schouten callout_stop(&conscallout); 569c9dba40cSEd Schouten while ((c = msgbuf_getchar(&consmsgbuf)) != -1) 570c9dba40cSEd Schouten cnputc(c); 571c9dba40cSEd Schouten free(consbuf, M_TTYCONS); 572c9dba40cSEd Schouten consbuf = NULL; 573c9dba40cSEd Schouten } 574c9dba40cSEd Schouten 575c9dba40cSEd Schouten /* Times per second to check for pending console tty messages. */ 576c9dba40cSEd Schouten static int constty_wakeups_per_second = 5; 577c9dba40cSEd Schouten SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, 578a0c87b74SGavin Atkinson &constty_wakeups_per_second, 0, 579a0c87b74SGavin Atkinson "Times per second to check for pending console tty messages"); 580c9dba40cSEd Schouten 581c9dba40cSEd Schouten static void 582c9dba40cSEd Schouten constty_timeout(void *arg) 583c9dba40cSEd Schouten { 584c9dba40cSEd Schouten int c; 585c9dba40cSEd Schouten 586c9dba40cSEd Schouten if (constty != NULL) { 587c9dba40cSEd Schouten tty_lock(constty); 588c9dba40cSEd Schouten while ((c = msgbuf_getchar(&consmsgbuf)) != -1) { 589c9dba40cSEd Schouten if (tty_putchar(constty, c) < 0) { 590c9dba40cSEd Schouten tty_unlock(constty); 591c9dba40cSEd Schouten constty = NULL; 592c9dba40cSEd Schouten break; 593c9dba40cSEd Schouten } 594c9dba40cSEd Schouten } 595c9dba40cSEd Schouten 596c9dba40cSEd Schouten if (constty != NULL) 597c9dba40cSEd Schouten tty_unlock(constty); 598c9dba40cSEd Schouten } 599c9dba40cSEd Schouten if (constty != NULL) { 600c9dba40cSEd Schouten callout_reset(&conscallout, hz / constty_wakeups_per_second, 601c9dba40cSEd Schouten constty_timeout, NULL); 602c9dba40cSEd Schouten } else { 603c9dba40cSEd Schouten /* Deallocate the constty buffer memory. */ 604c9dba40cSEd Schouten constty_clear(); 605c9dba40cSEd Schouten } 606c9dba40cSEd Schouten } 607c9dba40cSEd Schouten 608c9dba40cSEd Schouten static void 609c9dba40cSEd Schouten cn_drvinit(void *unused) 610c9dba40cSEd Schouten { 611c9dba40cSEd Schouten 61204a8159dSHans Petter Selasky mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS); 613c9dba40cSEd Schouten use_cnputs_mtx = 1; 614c9dba40cSEd Schouten } 615c9dba40cSEd Schouten 616c9dba40cSEd Schouten SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL); 617c9dba40cSEd Schouten 618c9dba40cSEd Schouten /* 619c9dba40cSEd Schouten * Sysbeep(), if we have hardware for it 620c9dba40cSEd Schouten */ 621c9dba40cSEd Schouten 622c9dba40cSEd Schouten #ifdef HAS_TIMER_SPKR 623c9dba40cSEd Schouten 624c9dba40cSEd Schouten static int beeping; 625fadf3fb9SJohn Baldwin static struct callout beeping_timer; 626c9dba40cSEd Schouten 627c9dba40cSEd Schouten static void 628c9dba40cSEd Schouten sysbeepstop(void *chan) 629c9dba40cSEd Schouten { 630c9dba40cSEd Schouten 631c9dba40cSEd Schouten timer_spkr_release(); 632c9dba40cSEd Schouten beeping = 0; 633c9dba40cSEd Schouten } 634c9dba40cSEd Schouten 635c9dba40cSEd Schouten int 636c9dba40cSEd Schouten sysbeep(int pitch, int period) 637c9dba40cSEd Schouten { 638c9dba40cSEd Schouten 639c9dba40cSEd Schouten if (timer_spkr_acquire()) { 640c9dba40cSEd Schouten if (!beeping) { 641c9dba40cSEd Schouten /* Something else owns it. */ 642c9dba40cSEd Schouten return (EBUSY); 643c9dba40cSEd Schouten } 644c9dba40cSEd Schouten } 645c9dba40cSEd Schouten timer_spkr_setfreq(pitch); 646c9dba40cSEd Schouten if (!beeping) { 647c9dba40cSEd Schouten beeping = period; 648fadf3fb9SJohn Baldwin callout_reset(&beeping_timer, period, sysbeepstop, NULL); 649c9dba40cSEd Schouten } 650c9dba40cSEd Schouten return (0); 651c9dba40cSEd Schouten } 652c9dba40cSEd Schouten 653fadf3fb9SJohn Baldwin static void 654fadf3fb9SJohn Baldwin sysbeep_init(void *unused) 655fadf3fb9SJohn Baldwin { 656fadf3fb9SJohn Baldwin 657fadf3fb9SJohn Baldwin callout_init(&beeping_timer, CALLOUT_MPSAFE); 658fadf3fb9SJohn Baldwin } 659fadf3fb9SJohn Baldwin SYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL); 660c9dba40cSEd Schouten #else 661c9dba40cSEd Schouten 662c9dba40cSEd Schouten /* 663c9dba40cSEd Schouten * No hardware, no sound 664c9dba40cSEd Schouten */ 665c9dba40cSEd Schouten 666c9dba40cSEd Schouten int 667c9dba40cSEd Schouten sysbeep(int pitch __unused, int period __unused) 668c9dba40cSEd Schouten { 669c9dba40cSEd Schouten 670c9dba40cSEd Schouten return (ENODEV); 671c9dba40cSEd Schouten } 672c9dba40cSEd Schouten 673c9dba40cSEd Schouten #endif 674c9dba40cSEd Schouten 67559644098SEd Maste /* 67659644098SEd Maste * Temporary support for sc(4) to vt(4) transition. 67759644098SEd Maste */ 678018147eeSEd Maste static unsigned vty_prefer; 679af3b2549SHans Petter Selasky static char vty_name[16]; 680af3b2549SHans Petter Selasky SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, vty_name, 681af3b2549SHans Petter Selasky 0, "Console vty driver"); 68259644098SEd Maste 68359644098SEd Maste int 68459644098SEd Maste vty_enabled(unsigned vty) 68559644098SEd Maste { 68659644098SEd Maste static unsigned vty_selected = 0; 68759644098SEd Maste 68859644098SEd Maste if (vty_selected == 0) { 68959644098SEd Maste TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name)); 69059644098SEd Maste do { 69159644098SEd Maste #if defined(DEV_SC) 69259644098SEd Maste if (strcmp(vty_name, "sc") == 0) { 69359644098SEd Maste vty_selected = VTY_SC; 69459644098SEd Maste break; 69559644098SEd Maste } 69659644098SEd Maste #endif 69759644098SEd Maste #if defined(DEV_VT) 69859644098SEd Maste if (strcmp(vty_name, "vt") == 0) { 69959644098SEd Maste vty_selected = VTY_VT; 70059644098SEd Maste break; 70159644098SEd Maste } 70259644098SEd Maste #endif 703018147eeSEd Maste if (vty_prefer != 0) { 704018147eeSEd Maste vty_selected = vty_prefer; 705018147eeSEd Maste break; 706018147eeSEd Maste } 7072d6f6d63SJean-Sébastien Pédron #if defined(DEV_VT) 70859644098SEd Maste vty_selected = VTY_VT; 7092d6f6d63SJean-Sébastien Pédron #elif defined(DEV_SC) 7102d6f6d63SJean-Sébastien Pédron vty_selected = VTY_SC; 71159644098SEd Maste #endif 71259644098SEd Maste } while (0); 71359644098SEd Maste 71459644098SEd Maste if (vty_selected == VTY_VT) 71559644098SEd Maste strcpy(vty_name, "vt"); 71659644098SEd Maste else if (vty_selected == VTY_SC) 71759644098SEd Maste strcpy(vty_name, "sc"); 71859644098SEd Maste } 71959644098SEd Maste return ((vty_selected & vty) != 0); 72059644098SEd Maste } 72159644098SEd Maste 722018147eeSEd Maste void 723018147eeSEd Maste vty_set_preferred(unsigned vty) 724018147eeSEd Maste { 725018147eeSEd Maste 726018147eeSEd Maste vty_prefer = vty; 727018147eeSEd Maste #if !defined(DEV_SC) 728969d3cc2SEd Maste vty_prefer &= ~VTY_SC; 729018147eeSEd Maste #endif 730018147eeSEd Maste #if !defined(DEV_VT) 731969d3cc2SEd Maste vty_prefer &= ~VTY_VT; 732018147eeSEd Maste #endif 733018147eeSEd Maste } 734018147eeSEd Maste 735