]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/tty_cons.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the University of
21  *      California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *      from: @(#)cons.c        7.2 (Berkeley) 5/9/91
39  * $FreeBSD$
40  */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/conf.h>
45 #include <sys/cons.h>
46 #include <sys/kernel.h>
47 #include <sys/reboot.h>
48 #include <sys/sysctl.h>
49 #include <sys/proc.h>
50 #include <sys/tty.h>
51 #include <sys/uio.h>
52
53 #include <machine/cpu.h>
54
55 static  d_open_t        cnopen;
56 static  d_close_t       cnclose;
57 static  d_read_t        cnread;
58 static  d_write_t       cnwrite;
59 static  d_ioctl_t       cnioctl;
60 static  d_poll_t        cnpoll;
61
62 #define CDEV_MAJOR      0
63 static struct cdevsw cn_cdevsw = {
64         /* open */      cnopen,
65         /* close */     cnclose,
66         /* read */      cnread,
67         /* write */     cnwrite,
68         /* ioctl */     cnioctl,
69         /* poll */      cnpoll,
70         /* mmap */      nommap,
71         /* strategy */  nostrategy,
72         /* name */      "console",
73         /* maj */       CDEV_MAJOR,
74         /* dump */      nodump,
75         /* psize */     nopsize,
76         /* flags */     D_TTY,
77         /* bmaj */      -1
78 };
79
80 static dev_t    cn_dev_t;       /* seems to be never really used */
81 static udev_t   cn_udev_t;
82 SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
83         &cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
84
85 static int cn_mute;
86
87 int     cons_unavail = 0;       /* XXX:
88                                  * physical console not available for
89                                  * input (i.e., it is in graphics mode)
90                                  */
91
92 static u_char cn_is_open;               /* nonzero if logical console is open */
93 static int openmode, openflag;          /* how /dev/console was openned */
94 static dev_t cn_devfsdev;               /* represents the device private info */
95 static u_char cn_phys_is_open;          /* nonzero if physical device is open */
96 static d_close_t *cn_phys_close;        /* physical device close function */
97 static d_open_t *cn_phys_open;          /* physical device open function */
98        struct consdev *cn_tab;          /* physical console device info */
99
100 CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
101
102 void
103 cninit()
104 {
105         struct consdev *best_cp, *cp, **list;
106
107         /*
108          * Find the first console with the highest priority.
109          */
110         best_cp = NULL;
111         list = (struct consdev **)cons_set.ls_items;
112         while ((cp = *list++) != NULL) {
113                 if (cp->cn_probe == NULL)
114                         continue;
115                 (*cp->cn_probe)(cp);
116                 if (cp->cn_pri > CN_DEAD &&
117                     (best_cp == NULL || cp->cn_pri > best_cp->cn_pri))
118                         best_cp = cp;
119         }
120
121         /*
122          * Check if we should mute the console (for security reasons perhaps)
123          * It can be changes dynamically using sysctl kern.consmute
124          * once we are up and going.
125          * 
126          */
127         cn_mute = ((boothowto & (RB_MUTE
128                         |RB_SINGLE
129                         |RB_VERBOSE
130                         |RB_ASKNAME
131                         |RB_CONFIG)) == RB_MUTE);
132         
133         /*
134          * If no console, give up.
135          */
136         if (best_cp == NULL) {
137                 if (cn_tab != NULL && cn_tab->cn_term != NULL)
138                         (*cn_tab->cn_term)(cn_tab);
139                 cn_tab = best_cp;
140                 return;
141         }
142
143         /*
144          * Initialize console, then attach to it.  This ordering allows
145          * debugging using the previous console, if any.
146          */
147         (*best_cp->cn_init)(best_cp);
148         if (cn_tab != NULL && cn_tab != best_cp) {
149                 /* Turn off the previous console.  */
150                 if (cn_tab->cn_term != NULL)
151                         (*cn_tab->cn_term)(cn_tab);
152         }
153         cn_tab = best_cp;
154 }
155
156 void
157 cninit_finish()
158 {
159         struct cdevsw *cdp;
160
161         if ((cn_tab == NULL) || cn_mute)
162                 return;
163
164         /*
165          * Hook the open and close functions.
166          */
167         cdp = devsw(cn_tab->cn_dev);
168         if (cdp != NULL) {
169                 cn_phys_close = cdp->d_close;
170                 cdp->d_close = cnclose;
171                 cn_phys_open = cdp->d_open;
172                 cdp->d_open = cnopen;
173         }
174         cn_dev_t = cn_tab->cn_dev;
175         cn_udev_t = dev2udev(cn_dev_t);
176 }
177
178 static void
179 cnuninit(void)
180 {
181         struct cdevsw *cdp;
182
183         if (cn_tab == NULL)
184                 return;
185
186         /*
187          * Unhook the open and close functions.
188          */
189         cdp = devsw(cn_tab->cn_dev);
190         if (cdp != NULL) {
191                 cdp->d_close = cn_phys_close;
192                 cdp->d_open = cn_phys_open;
193         }
194         cn_phys_close = NULL;
195         cn_phys_open = NULL;
196         cn_dev_t = NODEV;
197         cn_udev_t = NOUDEV;
198 }
199
200 /*
201  * User has changed the state of the console muting.
202  * This may require us to open or close the device in question.
203  */
204 static int
205 sysctl_kern_consmute SYSCTL_HANDLER_ARGS
206 {
207         int error;
208         int ocn_mute;
209
210         ocn_mute = cn_mute;
211         error = sysctl_handle_int(oidp, &cn_mute, 0, req);
212         if((error == 0) && (cn_tab != NULL) && (req->newptr != NULL)) {
213                 if(ocn_mute && !cn_mute) {
214                         /*
215                          * going from muted to unmuted.. open the physical dev 
216                          * if the console has been openned
217                          */
218                         cninit_finish();
219                         if(cn_is_open)
220                                 /* XXX curproc is not what we want really */
221                                 error = cnopen(cn_dev_t, openflag,
222                                         openmode, curproc);
223                         /* if it failed, back it out */
224                         if ( error != 0) cnuninit();
225                 } else if (!ocn_mute && cn_mute) {
226                         /*
227                          * going from unmuted to muted.. close the physical dev 
228                          * if it's only open via /dev/console
229                          */
230                         if(cn_is_open)
231                                 error = cnclose(cn_dev_t, openflag,
232                                         openmode, curproc);
233                         if ( error == 0) cnuninit();
234                 }
235                 if (error != 0) {
236                         /* 
237                          * back out the change if there was an error
238                          */
239                         cn_mute = ocn_mute;
240                 }
241         }
242         return (error);
243 }
244
245 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
246         0, sizeof cn_mute, sysctl_kern_consmute, "I", "");
247
248 static int
249 cnopen(dev, flag, mode, p)
250         dev_t dev;
251         int flag, mode;
252         struct proc *p;
253 {
254         dev_t cndev, physdev;
255         int retval = 0;
256
257         if (cn_tab == NULL || cn_phys_open == NULL)
258                 return (0);
259         cndev = cn_tab->cn_dev;
260         physdev = (major(dev) == major(cndev) ? dev : cndev);
261         /*
262          * If mute is active, then non console opens don't get here
263          * so we don't need to check for that. They 
264          * bypass this and go straight to the device.
265          */
266         if(!cn_mute)
267                 retval = (*cn_phys_open)(physdev, flag, mode, p);
268         if (retval == 0) {
269                 /* 
270                  * check if we openned it via /dev/console or 
271                  * via the physical entry (e.g. /dev/sio0).
272                  */
273                 if (dev == cndev)
274                         cn_phys_is_open = 1;
275                 else if (physdev == cndev) {
276                         openmode = mode;
277                         openflag = flag;
278                         cn_is_open = 1;
279                 }
280                 dev->si_tty = physdev->si_tty;
281         }
282         return (retval);
283 }
284
285 static int
286 cnclose(dev, flag, mode, p)
287         dev_t dev;
288         int flag, mode;
289         struct proc *p;
290 {
291         dev_t cndev;
292         struct tty *cn_tp;
293
294         if (cn_tab == NULL || cn_phys_open == NULL)
295                 return (0);
296         cndev = cn_tab->cn_dev;
297         cn_tp = cndev->si_tty;
298         /*
299          * act appropriatly depending on whether it's /dev/console
300          * or the pysical device (e.g. /dev/sio) that's being closed.
301          * in either case, don't actually close the device unless
302          * both are closed.
303          */
304         if (dev == cndev) {
305                 /* the physical device is about to be closed */
306                 cn_phys_is_open = 0;
307                 if (cn_is_open) {
308                         if (cn_tp) {
309                                 /* perform a ttyhalfclose() */
310                                 /* reset session and proc group */
311                                 cn_tp->t_pgrp = NULL;
312                                 cn_tp->t_session = NULL;
313                         }
314                         return (0);
315                 }
316         } else if (major(dev) != major(cndev)) {
317                 /* the logical console is about to be closed */
318                 cn_is_open = 0;
319                 if (cn_phys_is_open)
320                         return (0);
321                 dev = cndev;
322         }
323         if(cn_phys_close)
324                 return ((*cn_phys_close)(dev, flag, mode, p));
325         return (0);
326 }
327
328 static int
329 cnread(dev, uio, flag)
330         dev_t dev;
331         struct uio *uio;
332         int flag;
333 {
334
335         if (cn_tab == NULL || cn_phys_open == NULL)
336                 return (0);
337         dev = cn_tab->cn_dev;
338         return ((*devsw(dev)->d_read)(dev, uio, flag));
339 }
340
341 static int
342 cnwrite(dev, uio, flag)
343         dev_t dev;
344         struct uio *uio;
345         int flag;
346 {
347
348         if (cn_tab == NULL || cn_phys_open == NULL) {
349                 uio->uio_resid = 0; /* dump the data */
350                 return (0);
351         }
352         if (constty)
353                 dev = constty->t_dev;
354         else
355                 dev = cn_tab->cn_dev;
356         return ((*devsw(dev)->d_write)(dev, uio, flag));
357 }
358
359 static int
360 cnioctl(dev, cmd, data, flag, p)
361         dev_t dev;
362         u_long cmd;
363         caddr_t data;
364         int flag;
365         struct proc *p;
366 {
367         int error;
368
369         if (cn_tab == NULL || cn_phys_open == NULL)
370                 return (0);
371         /*
372          * Superuser can always use this to wrest control of console
373          * output from the "virtual" console.
374          */
375         if (cmd == TIOCCONS && constty) {
376                 error = suser(p);
377                 if (error)
378                         return (error);
379                 constty = NULL;
380                 return (0);
381         }
382         dev = cn_tab->cn_dev;
383         return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, p));
384 }
385
386 static int
387 cnpoll(dev, events, p)
388         dev_t dev;
389         int events;
390         struct proc *p;
391 {
392         if ((cn_tab == NULL) || cn_mute)
393                 return (1);
394
395         dev = cn_tab->cn_dev;
396
397         return ((*devsw(dev)->d_poll)(dev, events, p));
398 }
399
400 int
401 cngetc()
402 {
403         int c;
404         if ((cn_tab == NULL) || cn_mute)
405                 return (-1);
406         c = (*cn_tab->cn_getc)(cn_tab->cn_dev);
407         if (c == '\r') c = '\n'; /* console input is always ICRNL */
408         return (c);
409 }
410
411 int
412 cncheckc()
413 {
414         if ((cn_tab == NULL) || cn_mute)
415                 return (-1);
416         return ((*cn_tab->cn_checkc)(cn_tab->cn_dev));
417 }
418
419 void
420 cnputc(c)
421         register int c;
422 {
423         if ((cn_tab == NULL) || cn_mute)
424                 return;
425         if (c) {
426                 if (c == '\n')
427                         (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
428                 (*cn_tab->cn_putc)(cn_tab->cn_dev, c);
429         }
430 }
431
432 void
433 cndbctl(on)
434         int on;
435 {
436         static int refcount;
437
438         if (cn_tab == NULL)
439                 return;
440         if (!on)
441                 refcount--;
442         if (refcount == 0 && cn_tab->cn_dbctl != NULL)
443                 (*cn_tab->cn_dbctl)(cn_tab->cn_dev, on);
444         if (on)
445                 refcount++;
446 }
447
448 static void
449 cn_drvinit(void *unused)
450 {
451
452         cn_devfsdev = make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
453             "console");
454 }
455
456 SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)