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