kern_cons.c revision 179246
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: head/sys/kern/tty_cons.c 179246 2008-05-23 16:06:35Z ed $"); 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 69static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling"); 70 71static d_open_t cnopen; 72static d_close_t cnclose; 73static d_read_t cnread; 74static d_write_t cnwrite; 75static d_ioctl_t cnioctl; 76static d_poll_t cnpoll; 77static d_kqfilter_t cnkqfilter; 78 79static struct cdevsw cn_cdevsw = { 80 .d_version = D_VERSION, 81 .d_open = cnopen, 82 .d_close = cnclose, 83 .d_read = cnread, 84 .d_write = cnwrite, 85 .d_ioctl = cnioctl, 86 .d_poll = cnpoll, 87 .d_name = "console", 88 .d_flags = D_TTY | D_NEEDGIANT, 89 .d_kqfilter = cnkqfilter, 90}; 91 92struct cn_device { 93 STAILQ_ENTRY(cn_device) cnd_next; 94 struct vnode *cnd_vp; 95 struct consdev *cnd_cn; 96}; 97 98#define CNDEVPATHMAX 32 99#define CNDEVTAB_SIZE 4 100static struct cn_device cn_devtab[CNDEVTAB_SIZE]; 101static STAILQ_HEAD(, cn_device) cn_devlist = 102 STAILQ_HEAD_INITIALIZER(cn_devlist); 103 104#define CND_INVALID(cnd, td) \ 105 (cnd == NULL || cnd->cnd_vp == NULL || \ 106 (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1))) 107 108static dev_t cn_udev_t; 109SYSCTL_OPAQUE(_machdep, OID_AUTO, consdev, CTLFLAG_RD, 110 &cn_udev_t, sizeof cn_udev_t, "T,struct cdev *", ""); 111 112int cons_avail_mask = 0; /* Bit mask. Each registered low level console 113 * which is currently unavailable for inpit 114 * (i.e., if it is in graphics mode) will have 115 * this bit cleared. 116 */ 117static int cn_mute; 118static int openflag; /* how /dev/console was opened */ 119static int cn_is_open; 120static char *consbuf; /* buffer used by `consmsgbuf' */ 121static struct callout conscallout; /* callout for outputting to constty */ 122struct msgbuf consmsgbuf; /* message buffer for console tty */ 123static u_char console_pausing; /* pause after each line during probe */ 124static char *console_pausestr= 125"<pause; press any key to proceed to next line or '.' to end pause mode>"; 126struct tty *constty; /* pointer to console "window" tty */ 127static struct mtx cnputs_mtx; /* Mutex for cnputs(). */ 128static int use_cnputs_mtx = 0; /* != 0 if cnputs_mtx locking reqd. */ 129 130static void constty_timeout(void *arg); 131 132static struct consdev cons_consdev; 133DATA_SET(cons_set, cons_consdev); 134SET_DECLARE(cons_set, struct consdev); 135 136void 137cninit(void) 138{ 139 struct consdev *best_cn, *cn, **list; 140 141 /* 142 * Check if we should mute the console (for security reasons perhaps) 143 * It can be changes dynamically using sysctl kern.consmute 144 * once we are up and going. 145 * 146 */ 147 cn_mute = ((boothowto & (RB_MUTE 148 |RB_SINGLE 149 |RB_VERBOSE 150 |RB_ASKNAME)) == RB_MUTE); 151 152 /* 153 * Find the first console with the highest priority. 154 */ 155 best_cn = NULL; 156 SET_FOREACH(list, cons_set) { 157 cn = *list; 158 cnremove(cn); 159 if (cn->cn_probe == NULL) 160 continue; 161 cn->cn_probe(cn); 162 if (cn->cn_pri == CN_DEAD) 163 continue; 164 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) 165 best_cn = cn; 166 if (boothowto & RB_MULTIPLE) { 167 /* 168 * Initialize console, and attach to it. 169 */ 170 cn->cn_init(cn); 171 cnadd(cn); 172 } 173 } 174 if (best_cn == NULL) 175 return; 176 if ((boothowto & RB_MULTIPLE) == 0) { 177 best_cn->cn_init(best_cn); 178 cnadd(best_cn); 179 } 180 if (boothowto & RB_PAUSE) 181 console_pausing = 1; 182 /* 183 * Make the best console the preferred console. 184 */ 185 cnselect(best_cn); 186} 187 188void 189cninit_finish() 190{ 191 console_pausing = 0; 192} 193 194/* add a new physical console to back the virtual console */ 195int 196cnadd(struct consdev *cn) 197{ 198 struct cn_device *cnd; 199 int i; 200 201 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 202 if (cnd->cnd_cn == cn) 203 return (0); 204 for (i = 0; i < CNDEVTAB_SIZE; i++) { 205 cnd = &cn_devtab[i]; 206 if (cnd->cnd_cn == NULL) 207 break; 208 } 209 if (cnd->cnd_cn != NULL) 210 return (ENOMEM); 211 cnd->cnd_cn = cn; 212 if (cn->cn_name[0] == '\0') { 213 /* XXX: it is unclear if/where this print might output */ 214 printf("WARNING: console at %p has no name\n", cn); 215 } 216 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); 217 218 /* Add device to the active mask. */ 219 cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0); 220 221 return (0); 222} 223 224void 225cnremove(struct consdev *cn) 226{ 227 struct cn_device *cnd; 228 int i; 229 230 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 231 if (cnd->cnd_cn != cn) 232 continue; 233 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 234 if (cnd->cnd_vp != NULL) 235 vn_close(cnd->cnd_vp, openflag, NOCRED, NULL); 236 cnd->cnd_vp = NULL; 237 cnd->cnd_cn = NULL; 238 239 /* Remove this device from available mask. */ 240 for (i = 0; i < CNDEVTAB_SIZE; i++) 241 if (cnd == &cn_devtab[i]) { 242 cons_avail_mask &= ~(1 << i); 243 break; 244 } 245#if 0 246 /* 247 * XXX 248 * syscons gets really confused if console resources are 249 * freed after the system has initialized. 250 */ 251 if (cn->cn_term != NULL) 252 cn->cn_term(cn); 253#endif 254 return; 255 } 256} 257 258void 259cnselect(struct consdev *cn) 260{ 261 struct cn_device *cnd; 262 263 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 264 if (cnd->cnd_cn != cn) 265 continue; 266 if (cnd == STAILQ_FIRST(&cn_devlist)) 267 return; 268 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 269 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); 270 return; 271 } 272} 273 274void 275cnavailable(struct consdev *cn, int available) 276{ 277 int i; 278 279 for (i = 0; i < CNDEVTAB_SIZE; i++) { 280 if (cn_devtab[i].cnd_cn == cn) 281 break; 282 } 283 if (available) { 284 if (i < CNDEVTAB_SIZE) 285 cons_avail_mask |= (1 << i); 286 cn->cn_flags &= ~CN_FLAG_NOAVAIL; 287 } else { 288 if (i < CNDEVTAB_SIZE) 289 cons_avail_mask &= ~(1 << i); 290 cn->cn_flags |= CN_FLAG_NOAVAIL; 291 } 292} 293 294int 295cnunavailable(void) 296{ 297 298 return (cons_avail_mask == 0); 299} 300 301/* 302 * sysctl_kern_console() provides output parseable in conscontrol(1). 303 */ 304static int 305sysctl_kern_console(SYSCTL_HANDLER_ARGS) 306{ 307 struct cn_device *cnd; 308 struct consdev *cp, **list; 309 char *p; 310 int delete, error; 311 struct sbuf *sb; 312 313 sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND); 314 if (sb == NULL) 315 return (ENOMEM); 316 sbuf_clear(sb); 317 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 318 sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name); 319 sbuf_printf(sb, "/"); 320 SET_FOREACH(list, cons_set) { 321 cp = *list; 322 if (cp->cn_name[0] != '\0') 323 sbuf_printf(sb, "%s,", cp->cn_name); 324 } 325 sbuf_finish(sb); 326 error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req); 327 if (error == 0 && req->newptr != NULL) { 328 p = sbuf_data(sb); 329 error = ENXIO; 330 delete = 0; 331 if (*p == '-') { 332 delete = 1; 333 p++; 334 } 335 SET_FOREACH(list, cons_set) { 336 cp = *list; 337 if (strcmp(p, cp->cn_name) != 0) 338 continue; 339 if (delete) { 340 cnremove(cp); 341 error = 0; 342 } else { 343 error = cnadd(cp); 344 if (error == 0) 345 cnselect(cp); 346 } 347 break; 348 } 349 } 350 sbuf_delete(sb); 351 return (error); 352} 353 354SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW, 355 0, 0, sysctl_kern_console, "A", "Console device control"); 356 357/* 358 * User has changed the state of the console muting. 359 * This may require us to open or close the device in question. 360 */ 361static int 362sysctl_kern_consmute(SYSCTL_HANDLER_ARGS) 363{ 364 int error; 365 int ocn_mute; 366 367 ocn_mute = cn_mute; 368 error = sysctl_handle_int(oidp, &cn_mute, 0, req); 369 if (error != 0 || req->newptr == NULL) 370 return (error); 371 if (ocn_mute && !cn_mute && cn_is_open) 372 error = cnopen(NULL, openflag, 0, curthread); 373 else if (!ocn_mute && cn_mute && cn_is_open) { 374 error = cnclose(NULL, openflag, 0, curthread); 375 cn_is_open = 1; /* XXX hack */ 376 } 377 return (error); 378} 379 380SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW, 381 0, sizeof(cn_mute), sysctl_kern_consmute, "I", ""); 382 383static int 384cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen) 385{ 386 char path[CNDEVPATHMAX]; 387 struct nameidata nd; 388 struct vnode *vp; 389 struct cdev *dev; 390 struct cdevsw *csw; 391 int error; 392 393 if ((vp = cnd->cnd_vp) != NULL) { 394 if (!forceopen && vp->v_type != VBAD) { 395 dev = vp->v_rdev; 396 csw = dev_refthread(dev); 397 if (csw == NULL) 398 return (ENXIO); 399 error = (*csw->d_open)(dev, openflag, 0, td); 400 dev_relthread(dev); 401 return (error); 402 } 403 cnd->cnd_vp = NULL; 404 vn_close(vp, openflag, td->td_ucred, td); 405 } 406 snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_cn->cn_name); 407 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td); 408 error = vn_open(&nd, &openflag, 0, NULL); 409 if (error == 0) { 410 NDFREE(&nd, NDF_ONLY_PNBUF); 411 VOP_UNLOCK(nd.ni_vp, 0); 412 if (nd.ni_vp->v_type == VCHR) 413 cnd->cnd_vp = nd.ni_vp; 414 else 415 vn_close(nd.ni_vp, openflag, td->td_ucred, td); 416 } 417 return (cnd->cnd_vp != NULL); 418} 419 420static int 421cnopen(struct cdev *dev, int flag, int mode, struct thread *td) 422{ 423 struct cn_device *cnd; 424 425 openflag = flag | FWRITE; /* XXX */ 426 cn_is_open = 1; /* console is logically open */ 427 if (cn_mute) 428 return (0); 429 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 430 cn_devopen(cnd, td, 0); 431 return (0); 432} 433 434static int 435cnclose(struct cdev *dev, int flag, int mode, struct thread *td) 436{ 437 struct cn_device *cnd; 438 struct vnode *vp; 439 440 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 441 if ((vp = cnd->cnd_vp) == NULL) 442 continue; 443 cnd->cnd_vp = NULL; 444 vn_close(vp, openflag, td->td_ucred, td); 445 } 446 cn_is_open = 0; 447 return (0); 448} 449 450static int 451cnread(struct cdev *dev, struct uio *uio, int flag) 452{ 453 struct cn_device *cnd; 454 struct cdevsw *csw; 455 int error; 456 457 cnd = STAILQ_FIRST(&cn_devlist); 458 if (cn_mute || CND_INVALID(cnd, curthread)) 459 return (0); 460 dev = cnd->cnd_vp->v_rdev; 461 csw = dev_refthread(dev); 462 if (csw == NULL) 463 return (ENXIO); 464 error = (csw->d_read)(dev, uio, flag); 465 dev_relthread(dev); 466 return (error); 467} 468 469static int 470cnwrite(struct cdev *dev, struct uio *uio, int flag) 471{ 472 struct cn_device *cnd; 473 struct cdevsw *csw; 474 int error; 475 476 cnd = STAILQ_FIRST(&cn_devlist); 477 if (cn_mute || CND_INVALID(cnd, curthread)) 478 goto done; 479 if (constty) 480 dev = constty->t_dev; 481 else 482 dev = cnd->cnd_vp->v_rdev; 483 if (dev != NULL) { 484 log_console(uio); 485 csw = dev_refthread(dev); 486 if (csw == NULL) 487 return (ENXIO); 488 error = (csw->d_write)(dev, uio, flag); 489 dev_relthread(dev); 490 return (error); 491 } 492done: 493 uio->uio_resid = 0; /* dump the data */ 494 return (0); 495} 496 497static int 498cnioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 499{ 500 struct cn_device *cnd; 501 struct cdevsw *csw; 502 int error; 503 504 cnd = STAILQ_FIRST(&cn_devlist); 505 if (cn_mute || CND_INVALID(cnd, td)) 506 return (0); 507 /* 508 * Superuser can always use this to wrest control of console 509 * output from the "virtual" console. 510 */ 511 if (cmd == TIOCCONS && constty) { 512 error = priv_check(td, PRIV_TTY_CONSOLE); 513 if (error) 514 return (error); 515 constty = NULL; 516 return (0); 517 } 518 dev = cnd->cnd_vp->v_rdev; 519 if (dev == NULL) 520 return (0); /* XXX : ENOTTY ? */ 521 csw = dev_refthread(dev); 522 if (csw == NULL) 523 return (ENXIO); 524 error = (csw->d_ioctl)(dev, cmd, data, flag, td); 525 dev_relthread(dev); 526 return (error); 527} 528 529/* 530 * XXX 531 * poll/kqfilter do not appear to be correct 532 */ 533static int 534cnpoll(struct cdev *dev, int events, struct thread *td) 535{ 536 struct cn_device *cnd; 537 struct cdevsw *csw; 538 int error; 539 540 cnd = STAILQ_FIRST(&cn_devlist); 541 if (cn_mute || CND_INVALID(cnd, td)) 542 return (0); 543 dev = cnd->cnd_vp->v_rdev; 544 if (dev == NULL) 545 return (0); 546 csw = dev_refthread(dev); 547 if (csw == NULL) 548 return (ENXIO); 549 error = (csw->d_poll)(dev, events, td); 550 dev_relthread(dev); 551 return (error); 552} 553 554static int 555cnkqfilter(struct cdev *dev, struct knote *kn) 556{ 557 struct cn_device *cnd; 558 struct cdevsw *csw; 559 int error; 560 561 cnd = STAILQ_FIRST(&cn_devlist); 562 if (cn_mute || CND_INVALID(cnd, curthread)) 563 return (EINVAL); 564 dev = cnd->cnd_vp->v_rdev; 565 if (dev == NULL) 566 return (ENXIO); 567 csw = dev_refthread(dev); 568 if (csw == NULL) 569 return (ENXIO); 570 error = (csw->d_kqfilter)(dev, kn); 571 dev_relthread(dev); 572 return (error); 573} 574 575/* 576 * Low level console routines. 577 */ 578int 579cngetc(void) 580{ 581 int c; 582 583 if (cn_mute) 584 return (-1); 585 while ((c = cncheckc()) == -1) 586 ; 587 if (c == '\r') 588 c = '\n'; /* console input is always ICRNL */ 589 return (c); 590} 591 592int 593cncheckc(void) 594{ 595 struct cn_device *cnd; 596 struct consdev *cn; 597 int c; 598 599 if (cn_mute) 600 return (-1); 601 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 602 cn = cnd->cnd_cn; 603 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 604 if (cn->cn_checkc != NULL) 605 c = cn->cn_checkc(cn); 606 else 607 c = cn->cn_getc(cn); 608 if (c != -1) { 609 return (c); 610 } 611 } 612 } 613 return (-1); 614} 615 616void 617cnputc(int c) 618{ 619 struct cn_device *cnd; 620 struct consdev *cn; 621 char *cp; 622 623 if (cn_mute || c == '\0') 624 return; 625 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 626 cn = cnd->cnd_cn; 627 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 628 if (c == '\n') 629 cn->cn_putc(cn, '\r'); 630 cn->cn_putc(cn, c); 631 } 632 } 633 if (console_pausing && c == '\n' && !kdb_active) { 634 for (cp = console_pausestr; *cp != '\0'; cp++) 635 cnputc(*cp); 636 if (cngetc() == '.') 637 console_pausing = 0; 638 cnputc('\r'); 639 for (cp = console_pausestr; *cp != '\0'; cp++) 640 cnputc(' '); 641 cnputc('\r'); 642 } 643} 644 645void 646cnputs(char *p) 647{ 648 int c; 649 int unlock_reqd = 0; 650 651 if (use_cnputs_mtx) { 652 mtx_lock_spin(&cnputs_mtx); 653 unlock_reqd = 1; 654 } 655 656 while ((c = *p++) != '\0') 657 cnputc(c); 658 659 if (unlock_reqd) 660 mtx_unlock_spin(&cnputs_mtx); 661} 662 663static int consmsgbuf_size = 8192; 664SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0, 665 ""); 666 667/* 668 * Redirect console output to a tty. 669 */ 670void 671constty_set(struct tty *tp) 672{ 673 int size; 674 675 KASSERT(tp != NULL, ("constty_set: NULL tp")); 676 if (consbuf == NULL) { 677 size = consmsgbuf_size; 678 consbuf = malloc(size, M_TTYCONS, M_WAITOK); 679 msgbuf_init(&consmsgbuf, consbuf, size); 680 callout_init(&conscallout, 0); 681 } 682 constty = tp; 683 constty_timeout(NULL); 684} 685 686/* 687 * Disable console redirection to a tty. 688 */ 689void 690constty_clear(void) 691{ 692 int c; 693 694 constty = NULL; 695 if (consbuf == NULL) 696 return; 697 callout_stop(&conscallout); 698 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) 699 cnputc(c); 700 free(consbuf, M_TTYCONS); 701 consbuf = NULL; 702} 703 704/* Times per second to check for pending console tty messages. */ 705static int constty_wakeups_per_second = 5; 706SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, 707 &constty_wakeups_per_second, 0, ""); 708 709static void 710constty_timeout(void *arg) 711{ 712 int c; 713 714 while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) { 715 if (tputchar(c, constty) < 0) 716 constty = NULL; 717 } 718 if (constty != NULL) { 719 callout_reset(&conscallout, hz / constty_wakeups_per_second, 720 constty_timeout, NULL); 721 } else { 722 /* Deallocate the constty buffer memory. */ 723 constty_clear(); 724 } 725} 726 727static void 728cn_drvinit(void *unused) 729{ 730 731 make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console"); 732 733 mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS); 734 use_cnputs_mtx = 1; 735} 736 737SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL); 738 739/* 740 * Sysbeep(), if we have hardware for it 741 */ 742 743#ifdef HAS_TIMER_SPKR 744 745static int beeping; 746 747static void 748sysbeepstop(void *chan) 749{ 750 751 timer_spkr_release(); 752 beeping = 0; 753} 754 755int 756sysbeep(int pitch, int period) 757{ 758 759 if (timer_spkr_acquire()) { 760 if (!beeping) { 761 /* Something else owns it. */ 762 return (EBUSY); 763 } 764 } 765 timer_spkr_setfreq(pitch); 766 if (!beeping) { 767 beeping = period; 768 timeout(sysbeepstop, (void *)NULL, period); 769 } 770 return (0); 771} 772 773#else 774 775/* 776 * No hardware, no sound 777 */ 778 779int 780sysbeep(int pitch __unused, int period __unused) 781{ 782 783 return (ENODEV); 784} 785 786#endif 787 788