1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1991 The Regents of the University of California. 6 * Copyright (c) 1999 Michael Smith 7 * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 8 * 9 * All rights reserved. 10 * 11 * This code is derived from software contributed to Berkeley by 12 * the Systems Programming Group of the University of Utah Computer 13 * Science Department. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 3. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40 #include <sys/cdefs.h> 41 #include "opt_ddb.h" 42 #include "opt_syscons.h" 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/lock.h> 47 #include <sys/mutex.h> 48 #include <sys/conf.h> 49 #include <sys/cons.h> 50 #include <sys/fcntl.h> 51 #include <sys/kbio.h> 52 #include <sys/kdb.h> 53 #include <sys/kernel.h> 54 #include <sys/malloc.h> 55 #include <sys/msgbuf.h> 56 #include <sys/namei.h> 57 #include <sys/priv.h> 58 #include <sys/proc.h> 59 #include <sys/queue.h> 60 #include <sys/reboot.h> 61 #include <sys/sysctl.h> 62 #include <sys/sbuf.h> 63 #include <sys/tslog.h> 64 #include <sys/tty.h> 65 #include <sys/uio.h> 66 #include <sys/vnode.h> 67 68 #include <ddb/ddb.h> 69 70 #include <dev/kbd/kbdreg.h> 71 72 #include <machine/cpu.h> 73 #include <machine/clock.h> 74 75 /* 76 * Check for 'options EARLY_PRINTF' that may have been used in old kernel 77 * config files. If you are hitting this error you should update your 78 * config to use 'options EARLY_PRINTF=<device name>', e.g. with the 79 * Arm pl011 use: 80 * 81 * options EARLY_PRINTF=pl011 82 */ 83 #if CHECK_EARLY_PRINTF(1) 84 #error Update your config to use 'options EARLY_PRINTF=<device name>' 85 #endif 86 87 static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling"); 88 89 struct cn_device { 90 STAILQ_ENTRY(cn_device) cnd_next; 91 struct consdev *cnd_cn; 92 }; 93 94 #define CNDEVPATHMAX 32 95 #define CNDEVTAB_SIZE 4 96 static struct cn_device cn_devtab[CNDEVTAB_SIZE]; 97 static STAILQ_HEAD(, cn_device) cn_devlist = 98 STAILQ_HEAD_INITIALIZER(cn_devlist); 99 100 int cons_avail_mask = 0; /* Bit mask. Each registered low level console 101 * which is currently unavailable for inpit 102 * (i.e., if it is in graphics mode) will have 103 * this bit cleared. 104 */ 105 106 static int cn_mute; 107 SYSCTL_INT(_kern, OID_AUTO, consmute, CTLFLAG_RW, &cn_mute, 0, 108 "State of the console muting"); 109 110 static char *consbuf; /* buffer used by `consmsgbuf' */ 111 static struct callout conscallout; /* callout for outputting to constty */ 112 struct msgbuf consmsgbuf; /* message buffer for console tty */ 113 static bool console_pausing; /* pause after each line during probe */ 114 static const char console_pausestr[] = 115 "<pause; press any key to proceed to next line or '.' to end pause mode>"; 116 struct tty *constty; /* pointer to console "window" tty */ 117 static struct mtx constty_mtx; /* Mutex for constty assignment. */ 118 MTX_SYSINIT(constty_mtx, &constty_mtx, "constty_mtx", MTX_DEF); 119 static struct mtx cnputs_mtx; /* Mutex for cnputs(). */ 120 MTX_SYSINIT(cnputs_mtx, &cnputs_mtx, "cnputs_mtx", MTX_SPIN | MTX_NOWITNESS); 121 122 static void constty_timeout(void *arg); 123 124 static struct consdev cons_consdev; 125 DATA_SET(cons_set, cons_consdev); 126 SET_DECLARE(cons_set, struct consdev); 127 128 /* 129 * Stub for configurations that don't actually have a keyboard driver. Inclusion 130 * of kbd.c is contingent on any number of keyboard/console drivers being 131 * present in the kernel; rather than trying to catch them all, we'll just 132 * maintain this weak kbdinit that will be overridden by the strong version in 133 * kbd.c if it's present. 134 */ 135 __weak_symbol void 136 kbdinit(void) 137 { 138 139 } 140 141 void 142 cninit(void) 143 { 144 struct consdev *best_cn, *cn, **list; 145 146 TSENTER(); 147 /* 148 * Check if we should mute the console (for security reasons perhaps) 149 * It can be changes dynamically using sysctl kern.consmute 150 * once we are up and going. 151 * 152 */ 153 cn_mute = ((boothowto & (RB_MUTE 154 |RB_SINGLE 155 |RB_VERBOSE 156 |RB_ASKNAME)) == RB_MUTE); 157 158 /* 159 * Bring up the kbd layer just in time for cnprobe. Console drivers 160 * have a dependency on kbd being ready, so this fits nicely between the 161 * machdep callers of cninit() and MI probing/initialization of consoles 162 * here. 163 */ 164 kbdinit(); 165 166 /* 167 * Find the first console with the highest priority. 168 */ 169 best_cn = NULL; 170 SET_FOREACH(list, cons_set) { 171 cn = *list; 172 cnremove(cn); 173 /* Skip cons_consdev. */ 174 if (cn->cn_ops == NULL) 175 continue; 176 cn->cn_ops->cn_probe(cn); 177 if (cn->cn_pri == CN_DEAD) 178 continue; 179 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) 180 best_cn = cn; 181 if (boothowto & RB_MULTIPLE) { 182 /* 183 * Initialize console, and attach to it. 184 */ 185 cn->cn_ops->cn_init(cn); 186 cnadd(cn); 187 } 188 } 189 if (best_cn == NULL) 190 return; 191 if ((boothowto & RB_MULTIPLE) == 0) { 192 best_cn->cn_ops->cn_init(best_cn); 193 cnadd(best_cn); 194 } 195 if (boothowto & RB_PAUSE) 196 console_pausing = true; 197 /* 198 * Make the best console the preferred console. 199 */ 200 cnselect(best_cn); 201 202 #ifdef EARLY_PRINTF 203 /* 204 * Release early console. 205 */ 206 early_putc = NULL; 207 #endif 208 TSEXIT(); 209 } 210 211 void 212 cninit_finish(void) 213 { 214 console_pausing = false; 215 } 216 217 /* add a new physical console to back the virtual console */ 218 int 219 cnadd(struct consdev *cn) 220 { 221 struct cn_device *cnd; 222 int i; 223 224 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 225 if (cnd->cnd_cn == cn) 226 return (0); 227 for (i = 0; i < CNDEVTAB_SIZE; i++) { 228 cnd = &cn_devtab[i]; 229 if (cnd->cnd_cn == NULL) 230 break; 231 } 232 if (cnd->cnd_cn != NULL) 233 return (ENOMEM); 234 cnd->cnd_cn = cn; 235 if (cn->cn_name[0] == '\0') { 236 /* XXX: it is unclear if/where this print might output */ 237 printf("WARNING: console at %p has no name\n", cn); 238 } 239 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); 240 if (STAILQ_FIRST(&cn_devlist) == cnd) 241 ttyconsdev_select(cnd->cnd_cn->cn_name); 242 243 /* Add device to the active mask. */ 244 cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0); 245 246 return (0); 247 } 248 249 void 250 cnremove(struct consdev *cn) 251 { 252 struct cn_device *cnd; 253 int i; 254 255 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 256 if (cnd->cnd_cn != cn) 257 continue; 258 if (STAILQ_FIRST(&cn_devlist) == cnd) 259 ttyconsdev_select(NULL); 260 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 261 cnd->cnd_cn = NULL; 262 263 /* Remove this device from available mask. */ 264 for (i = 0; i < CNDEVTAB_SIZE; i++) 265 if (cnd == &cn_devtab[i]) { 266 cons_avail_mask &= ~(1 << i); 267 break; 268 } 269 #if 0 270 /* 271 * XXX 272 * syscons gets really confused if console resources are 273 * freed after the system has initialized. 274 */ 275 if (cn->cn_term != NULL) 276 cn->cn_ops->cn_term(cn); 277 #endif 278 return; 279 } 280 } 281 282 void 283 cnselect(struct consdev *cn) 284 { 285 struct cn_device *cnd; 286 287 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 288 if (cnd->cnd_cn != cn) 289 continue; 290 if (cnd == STAILQ_FIRST(&cn_devlist)) 291 return; 292 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 293 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); 294 ttyconsdev_select(cnd->cnd_cn->cn_name); 295 return; 296 } 297 } 298 299 void 300 cnavailable(struct consdev *cn, int available) 301 { 302 int i; 303 304 for (i = 0; i < CNDEVTAB_SIZE; i++) { 305 if (cn_devtab[i].cnd_cn == cn) 306 break; 307 } 308 if (available) { 309 if (i < CNDEVTAB_SIZE) 310 cons_avail_mask |= (1 << i); 311 cn->cn_flags &= ~CN_FLAG_NOAVAIL; 312 } else { 313 if (i < CNDEVTAB_SIZE) 314 cons_avail_mask &= ~(1 << i); 315 cn->cn_flags |= CN_FLAG_NOAVAIL; 316 } 317 } 318 319 int 320 cnunavailable(void) 321 { 322 323 return (cons_avail_mask == 0); 324 } 325 326 /* 327 * sysctl_kern_console() provides output parseable in conscontrol(1). 328 */ 329 static int 330 sysctl_kern_console(SYSCTL_HANDLER_ARGS) 331 { 332 struct cn_device *cnd; 333 struct consdev *cp, **list; 334 char *p; 335 bool delete; 336 int error; 337 struct sbuf *sb; 338 339 sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND | 340 SBUF_INCLUDENUL); 341 if (sb == NULL) 342 return (ENOMEM); 343 sbuf_clear(sb); 344 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 345 sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name); 346 sbuf_putc(sb, '/'); 347 SET_FOREACH(list, cons_set) { 348 cp = *list; 349 if (cp->cn_name[0] != '\0') 350 sbuf_printf(sb, "%s,", cp->cn_name); 351 } 352 sbuf_finish(sb); 353 error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req); 354 if (error == 0 && req->newptr != NULL) { 355 p = sbuf_data(sb); 356 error = ENXIO; 357 delete = false; 358 if (*p == '-') { 359 delete = true; 360 p++; 361 } 362 SET_FOREACH(list, cons_set) { 363 cp = *list; 364 if (strcmp(p, cp->cn_name) != 0) 365 continue; 366 if (delete) { 367 cnremove(cp); 368 error = 0; 369 } else { 370 error = cnadd(cp); 371 if (error == 0) 372 cnselect(cp); 373 } 374 break; 375 } 376 } 377 sbuf_delete(sb); 378 return (error); 379 } 380 381 SYSCTL_PROC(_kern, OID_AUTO, console, 382 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 0, 0, 383 sysctl_kern_console, "A", 384 "Console device control"); 385 386 void 387 cngrab(void) 388 { 389 struct cn_device *cnd; 390 struct consdev *cn; 391 392 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 393 cn = cnd->cnd_cn; 394 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) 395 cn->cn_ops->cn_grab(cn); 396 } 397 } 398 399 void 400 cnungrab(void) 401 { 402 struct cn_device *cnd; 403 struct consdev *cn; 404 405 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 406 cn = cnd->cnd_cn; 407 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) 408 cn->cn_ops->cn_ungrab(cn); 409 } 410 } 411 412 void 413 cnresume(void) 414 { 415 struct cn_device *cnd; 416 struct consdev *cn; 417 418 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 419 cn = cnd->cnd_cn; 420 if (cn->cn_ops->cn_resume != NULL) 421 cn->cn_ops->cn_resume(cn); 422 } 423 } 424 425 /* 426 * Low level console routines. 427 */ 428 int 429 cngetc(void) 430 { 431 int c; 432 433 if (cn_mute) 434 return (-1); 435 while ((c = cncheckc()) == -1) 436 cpu_spinwait(); 437 if (c == '\r') 438 c = '\n'; /* console input is always ICRNL */ 439 return (c); 440 } 441 442 int 443 cncheckc(void) 444 { 445 struct cn_device *cnd; 446 struct consdev *cn; 447 int c; 448 449 if (cn_mute) 450 return (-1); 451 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 452 cn = cnd->cnd_cn; 453 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 454 c = cn->cn_ops->cn_getc(cn); 455 if (c != -1) 456 return (c); 457 } 458 } 459 return (-1); 460 } 461 462 void 463 cngets(char *cp, size_t size, int visible) 464 { 465 char *lp, *end; 466 int c; 467 468 cngrab(); 469 470 lp = cp; 471 end = cp + size - 1; 472 for (;;) { 473 c = cngetc() & 0177; 474 switch (c) { 475 case '\n': 476 case '\r': 477 cnputc(c); 478 *lp = '\0'; 479 cnungrab(); 480 return; 481 case '\b': 482 case '\177': 483 if (lp > cp) { 484 if (visible) 485 cnputs("\b \b"); 486 lp--; 487 } 488 continue; 489 case '\0': 490 continue; 491 default: 492 if (lp < end) { 493 switch (visible) { 494 case GETS_NOECHO: 495 break; 496 case GETS_ECHOPASS: 497 cnputc('*'); 498 break; 499 default: 500 cnputc(c); 501 break; 502 } 503 *lp++ = c; 504 } 505 } 506 } 507 } 508 509 void 510 cnputc(int c) 511 { 512 struct cn_device *cnd; 513 struct consdev *cn; 514 const char *cp; 515 516 #ifdef EARLY_PRINTF 517 if (early_putc != NULL) { 518 if (c == '\n') 519 early_putc('\r'); 520 early_putc(c); 521 return; 522 } 523 #endif 524 525 if (cn_mute || c == '\0') 526 return; 527 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 528 cn = cnd->cnd_cn; 529 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 530 if (c == '\n') 531 cn->cn_ops->cn_putc(cn, '\r'); 532 cn->cn_ops->cn_putc(cn, c); 533 } 534 } 535 if (console_pausing && c == '\n' && !kdb_active) { 536 for (cp = console_pausestr; *cp != '\0'; cp++) 537 cnputc(*cp); 538 cngrab(); 539 if (cngetc() == '.') 540 console_pausing = false; 541 cnungrab(); 542 cnputc('\r'); 543 for (cp = console_pausestr; *cp != '\0'; cp++) 544 cnputc(' '); 545 cnputc('\r'); 546 } 547 } 548 549 void 550 cnputsn(const char *p, size_t n) 551 { 552 size_t i; 553 bool unlock_reqd = false; 554 555 if (mtx_initialized(&cnputs_mtx)) { 556 /* 557 * NOTE: Debug prints and/or witness printouts in 558 * console driver clients can cause the "cnputs_mtx" 559 * mutex to recurse. Simply return if that happens. 560 */ 561 if (mtx_owned(&cnputs_mtx)) 562 return; 563 mtx_lock_spin(&cnputs_mtx); 564 unlock_reqd = true; 565 } 566 567 for (i = 0; i < n; i++) 568 cnputc(p[i]); 569 570 if (unlock_reqd) 571 mtx_unlock_spin(&cnputs_mtx); 572 } 573 574 void 575 cnputs(const char *p) 576 { 577 cnputsn(p, strlen(p)); 578 } 579 580 static unsigned int consmsgbuf_size = 65536; 581 SYSCTL_UINT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RWTUN, &consmsgbuf_size, 582 0, "Console tty buffer size"); 583 584 /* 585 * Redirect console output to a tty. 586 */ 587 int 588 constty_set(struct tty *tp) 589 { 590 int size = consmsgbuf_size; 591 void *buf = NULL; 592 593 tty_assert_locked(tp); 594 if (constty == tp) 595 return (0); 596 if (constty != NULL) 597 return (EBUSY); 598 599 if (consbuf == NULL) { 600 tty_unlock(tp); 601 buf = malloc(size, M_TTYCONS, M_WAITOK); 602 tty_lock(tp); 603 } 604 mtx_lock(&constty_mtx); 605 if (constty != NULL) { 606 mtx_unlock(&constty_mtx); 607 free(buf, M_TTYCONS); 608 return (EBUSY); 609 } 610 if (consbuf == NULL) { 611 consbuf = buf; 612 msgbuf_init(&consmsgbuf, buf, size); 613 } else 614 free(buf, M_TTYCONS); 615 constty = tp; 616 mtx_unlock(&constty_mtx); 617 618 callout_init_mtx(&conscallout, tty_getlock(tp), 0); 619 constty_timeout(tp); 620 return (0); 621 } 622 623 /* 624 * Disable console redirection to a tty. 625 */ 626 int 627 constty_clear(struct tty *tp) 628 { 629 int c; 630 631 tty_assert_locked(tp); 632 if (constty != tp) 633 return (ENXIO); 634 callout_stop(&conscallout); 635 mtx_lock(&constty_mtx); 636 constty = NULL; 637 mtx_unlock(&constty_mtx); 638 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) 639 cnputc(c); 640 /* We never free consbuf because it can still be in use. */ 641 return (0); 642 } 643 644 /* Times per second to check for pending console tty messages. */ 645 static int constty_wakeups_per_second = 15; 646 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, 647 &constty_wakeups_per_second, 0, 648 "Times per second to check for pending console tty messages"); 649 650 static void 651 constty_timeout(void *arg) 652 { 653 struct tty *tp = arg; 654 int c; 655 656 tty_assert_locked(tp); 657 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) { 658 if (tty_putchar(tp, c) < 0) { 659 constty_clear(tp); 660 return; 661 } 662 } 663 callout_reset_sbt(&conscallout, SBT_1S / constty_wakeups_per_second, 664 0, constty_timeout, tp, C_PREL(1)); 665 } 666 667 /* 668 * Sysbeep(), if we have hardware for it 669 */ 670 671 #ifdef HAS_TIMER_SPKR 672 673 static bool beeping; 674 static struct callout beeping_timer; 675 676 static void 677 sysbeepstop(void *chan) 678 { 679 680 timer_spkr_release(); 681 beeping = false; 682 } 683 684 int 685 sysbeep(int pitch, sbintime_t duration) 686 { 687 688 if (timer_spkr_acquire()) { 689 if (!beeping) { 690 /* Something else owns it. */ 691 return (EBUSY); 692 } 693 } 694 timer_spkr_setfreq(pitch); 695 if (!beeping) { 696 beeping = true; 697 callout_reset_sbt(&beeping_timer, duration, 0, sysbeepstop, 698 NULL, C_PREL(5)); 699 } 700 return (0); 701 } 702 703 static void 704 sysbeep_init(void *unused) 705 { 706 707 callout_init(&beeping_timer, 1); 708 } 709 SYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL); 710 #else 711 712 /* 713 * No hardware, no sound 714 */ 715 716 int 717 sysbeep(int pitch __unused, sbintime_t duration __unused) 718 { 719 720 return (ENODEV); 721 } 722 723 #endif 724 725 /* 726 * Temporary support for sc(4) to vt(4) transition. 727 */ 728 static unsigned vty_prefer; 729 static char vty_name[16]; 730 SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, vty_name, 731 0, "Console vty driver"); 732 733 int 734 vty_enabled(unsigned vty) 735 { 736 static unsigned vty_selected = 0; 737 738 if (vty_selected == 0) { 739 TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name)); 740 do { 741 #if defined(DEV_SC) 742 if (strcmp(vty_name, "sc") == 0) { 743 vty_selected = VTY_SC; 744 break; 745 } 746 #endif 747 #if defined(DEV_VT) 748 if (strcmp(vty_name, "vt") == 0) { 749 vty_selected = VTY_VT; 750 break; 751 } 752 #endif 753 if (vty_prefer != 0) { 754 vty_selected = vty_prefer; 755 break; 756 } 757 #if defined(DEV_VT) 758 vty_selected = VTY_VT; 759 #elif defined(DEV_SC) 760 vty_selected = VTY_SC; 761 #endif 762 } while (0); 763 764 if (vty_selected == VTY_VT) 765 strcpy(vty_name, "vt"); 766 else if (vty_selected == VTY_SC) 767 strcpy(vty_name, "sc"); 768 } 769 return ((vty_selected & vty) != 0); 770 } 771 772 void 773 vty_set_preferred(unsigned vty) 774 { 775 776 vty_prefer = vty; 777 #if !defined(DEV_SC) 778 vty_prefer &= ~VTY_SC; 779 #endif 780 #if !defined(DEV_VT) 781 vty_prefer &= ~VTY_VT; 782 #endif 783 } 784