7f6e559b25d903642ebc71320795a3f858fd0122
[dragonfly.git] / sys / platform / vkernel / platform / console.c
1 /*
2  * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sys/platform/vkernel/platform/console.c,v 1.15 2007/05/28 05:26:29 dillon Exp $
35  */
36
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/conf.h>
40 #include <sys/cons.h>
41 #include <sys/tty.h>
42 #include <sys/termios.h>
43 #include <sys/fcntl.h>
44 #include <sys/signalvar.h>
45 #include <machine/md_var.h>
46 #include <unistd.h>
47 #include <termios.h>
48 #include <stdlib.h>
49
50 static int console_stolen_by_kernel;
51 static struct kqueue_info *kqueue_console_info;
52
53 /*
54  * Global console locking functions
55  */
56 void
57 cons_lock(void)
58 {
59 }
60
61 void
62 cons_unlock(void)
63 {
64 }
65
66 /************************************************************************
67  *                          CONSOLE DEVICE                              *
68  ************************************************************************
69  *
70  */
71
72 #define CDEV_MAJOR      12      /* steal the same major as /dev/ttyv* */
73
74 static int vcons_tty_param(struct tty *tp, struct termios *tio);
75 static void vcons_tty_start(struct tty *tp);
76 static void vcons_intr(void *tpx, struct intrframe *frame __unused);
77
78 static d_open_t         vcons_open;
79 static d_close_t        vcons_close;
80 static d_ioctl_t        vcons_ioctl;
81
82 static struct dev_ops vcons_ops = {
83         { "vcons", CDEV_MAJOR, D_TTY },
84         .d_open =       vcons_open,
85         .d_close =      vcons_close,
86         .d_read =       ttyread,
87         .d_write =      ttywrite,
88         .d_ioctl =      vcons_ioctl,
89         .d_poll =       ttypoll,
90 };
91
92 static int
93 vcons_open(struct dev_open_args *ap)
94 {
95         cdev_t dev = ap->a_head.a_dev;
96         struct tty *tp;
97         int error;
98
99         tp = dev->si_tty = ttymalloc(dev->si_tty);
100         if ((tp->t_state & TS_ISOPEN) == 0) {
101                 tp->t_oproc = vcons_tty_start;
102                 tp->t_param = vcons_tty_param;
103                 tp->t_stop = nottystop;
104                 tp->t_dev = dev;
105
106                 tp->t_state |= TS_CARR_ON | TS_CONNECTED;
107                 ttychars(tp);
108                 tp->t_iflag = TTYDEF_IFLAG;
109                 tp->t_oflag = TTYDEF_OFLAG;
110                 tp->t_cflag = TTYDEF_CFLAG;
111                 tp->t_lflag = TTYDEF_LFLAG;
112                 tp->t_ispeed = TTYDEF_SPEED;
113                 tp->t_ospeed = TTYDEF_SPEED;
114                 ttsetwater(tp);
115         }
116         if (minor(dev) == 0) {
117                 error = (*linesw[tp->t_line].l_open)(dev, tp);
118                 ioctl(0, TIOCGWINSZ, &tp->t_winsize);
119
120                 if (kqueue_console_info == NULL)
121                         kqueue_console_info = kqueue_add(0, vcons_intr, tp);
122         } else {
123                 /* dummy up other minors so the installer will run */
124                 error = 0;
125         }
126         return(error);
127 }
128
129 static int
130 vcons_close(struct dev_close_args *ap)
131 {
132         cdev_t dev = ap->a_head.a_dev;
133         struct tty *tp;
134
135         tp = dev->si_tty;
136         (*linesw[tp->t_line].l_close)(tp, ap->a_fflag);
137         ttyclose(tp);
138         return(0);
139 }
140
141 static int
142 vcons_ioctl(struct dev_ioctl_args *ap)
143 {
144         cdev_t dev = ap->a_head.a_dev;
145         struct tty *tp;
146         int error;
147
148         tp = dev->si_tty;
149         error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
150                                               ap->a_fflag, ap->a_cred);
151         if (error != ENOIOCTL)
152                 return (error);
153         error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
154         if (error != ENOIOCTL)
155                 return (error);
156         return (ENOTTY);
157 }
158
159 static int
160 vcons_tty_param(struct tty *tp, struct termios *tio)
161 {
162         tp->t_ispeed = tio->c_ispeed;
163         tp->t_ospeed = tio->c_ospeed;
164         tp->t_cflag = tio->c_cflag;
165         return(0);
166 }
167
168 static void
169 vcons_tty_start(struct tty *tp)
170 {
171         int n;
172         char buf[64];
173
174         if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
175                 ttwwakeup(tp);
176                 return;
177         }
178         tp->t_state |= TS_BUSY;
179         while ((n = q_to_b(&tp->t_outq, buf, sizeof(buf))) > 0) {
180                 /*
181                  * Dummy up ttyv1, etc.
182                  */
183                 if (minor(tp->t_dev) == 0)
184                         write(1, buf, n);
185         }
186         tp->t_state &= ~TS_BUSY;
187         ttwwakeup(tp);
188 }
189
190 static
191 void
192 vcons_intr(void *tpx, struct intrframe *frame __unused)
193 {
194         struct tty *tp = tpx;
195         unsigned char buf[32];
196         int i;
197         int n;
198
199         /*
200          * If we aren't open we only have synchronous traffic via the
201          * debugger and do not need to poll.
202          */
203         if ((tp->t_state & TS_ISOPEN) == 0)
204                 return;
205
206         /*
207          * Only poll if we are open and haven't been stolen by the debugger.
208          */
209         if (console_stolen_by_kernel == 0 && (tp->t_state & TS_ISOPEN)) {
210                 do {
211                         n = extpread(0, buf, sizeof(buf), O_FNONBLOCKING, -1LL);
212                         for (i = 0; i < n; ++i)
213                                 (*linesw[tp->t_line].l_rint)(buf[i], tp);
214                 } while (n > 0);
215         }
216 }
217
218 /************************************************************************
219  *                      KERNEL CONSOLE INTERFACE                        *
220  ************************************************************************
221  *
222  * Kernel direct-call interface console driver
223  */
224 static cn_probe_t       vconsprobe;
225 static cn_init_t        vconsinit;
226 static cn_init_fini_t   vconsinit_fini;
227 static cn_term_t        vconsterm;
228 static cn_getc_t        vconsgetc;
229 static cn_checkc_t      vconscheckc;
230 static cn_putc_t        vconsputc;
231
232 CONS_DRIVER(vcons, vconsprobe, vconsinit, vconsinit_fini, vconsterm, vconsgetc, 
233                 vconscheckc, vconsputc, NULL);
234
235 static struct termios init_tio;
236 static struct consdev *vconsole;
237
238 static void
239 vconsprobe(struct consdev *cp)
240 {
241         cp->cn_pri = CN_NORMAL;
242         cp->cn_probegood = 1;
243 }
244
245 /*
246  * This is a little bulky handler to set proper terminal
247  * settings in the case of a signal which might lead to
248  * termination or suspension.
249  */
250 static void
251 vconssignal(int sig)
252 {
253         struct termios curtio;
254         struct sigaction sa, osa;
255         sigset_t ss, oss;
256
257         tcgetattr(0, &curtio);
258         tcsetattr(0, TCSAFLUSH, &init_tio);
259         bzero(&sa, sizeof(sa));
260         sigemptyset(&sa.sa_mask);
261         sa.sa_handler = SIG_DFL;
262         sigaction(sig, &sa, &osa);
263         sigemptyset(&ss);
264         sigaddset(&ss, sig);
265         sigprocmask(SIG_UNBLOCK, &ss, &oss);
266         raise(sig);     /* now hand down the sig */
267         sigprocmask(SIG_SETMASK, &oss, NULL);
268         sigaction(sig, &osa, NULL);
269         tcsetattr(0, TCSAFLUSH, &curtio);
270 }
271
272 static void
273 vconswinch(int __unused sig)
274 {
275         struct winsize newsize;
276
277         if (vconsole != NULL && vconsole->cn_dev->si_tty != NULL) {
278                 ioctl(0, TIOCGWINSZ, &newsize);
279                 /*
280                  * ttioctl(vconsole->cn_dev->si_tty, TIOCSWINSZ, &newsize, 0);
281                  * I wished.  Unfortunately this needs a curproc, so do it
282                  * manually.
283                  */
284                 if (bcmp(&newsize, &vconsole->cn_dev->si_tty->t_winsize,
285                          sizeof(newsize)) != 0) {
286                         vconsole->cn_dev->si_tty->t_winsize = newsize;
287                         pgsignal(vconsole->cn_dev->si_tty->t_pgrp, SIGWINCH, 1);
288                 }
289         }
290 }
291
292 static void
293 vconscleanup(void)
294 {
295         tcsetattr(0, TCSAFLUSH, &init_tio);
296 }
297
298 static void
299 vconsinit(struct consdev *cp)
300 {
301         struct sigaction sa;
302
303         vconsole = cp;
304
305         tcgetattr(0, &init_tio);
306         bzero(&sa, sizeof(sa));
307         sigemptyset(&sa.sa_mask);
308         sa.sa_handler = vconssignal;
309         sigaction(SIGTSTP, &sa, NULL);
310         sigaction(SIGINT, &sa, NULL);
311         sigaction(SIGTERM, &sa, NULL);
312         atexit(vconscleanup);
313
314         sa.sa_handler = vconswinch;
315         sigaction(SIGWINCH, &sa, NULL);
316
317         vcons_set_mode(0);
318 }
319
320 static void
321 vconsinit_fini(struct consdev *cp)
322 {
323         cdev_t dev;
324         int i;
325
326         /*
327          * Implement ttyv0-ttyv7.  At the moment ttyv1-7 are sink nulls.
328          */
329         dev_ops_add(&vcons_ops, -1 & ~7, 0);
330         for (i = 0; i < 8; ++i) {
331                 dev = make_dev(&vcons_ops, i,
332                                UID_ROOT, GID_WHEEL, 0600, "ttyv%d", i);
333                 if (i == 0)
334                         cp->cn_dev = dev;
335         }
336 }
337
338 static void
339 vconsterm(struct consdev *vp)
340 {
341         vconsole = NULL;
342         vconscleanup();
343 }
344
345 static int
346 vconsgetc(void *private)
347 {
348         unsigned char c;
349         ssize_t n;
350
351         console_stolen_by_kernel = 1;
352         for (;;) {
353                 if ((n = read(0, &c, 1)) == 1)
354                         break;
355                 if (n < 0 && errno == EINTR)
356                         continue;
357                 panic("vconsgetc: EOF on console %d %d", n ,errno);
358         }
359         console_stolen_by_kernel = 0;
360         return((int)c);
361 }
362
363 static int
364 vconscheckc(void *private)
365 {
366         unsigned char c;
367
368         if (extpread(0, &c, 1, O_FNONBLOCKING, -1LL) == 1)
369                 return((int)c);
370         return(-1);
371 }
372
373 static void
374 vconsputc(void *private, int c)
375 {
376         char cc = c;
377
378         write(1, &cc, 1);
379 }
380
381 void
382 vcons_set_mode(int in_debugger)
383 {
384         struct termios tio;
385
386         if (tcgetattr(0, &tio) < 0)
387                 return;
388         cfmakeraw(&tio);
389         tio.c_oflag |= OPOST | ONLCR;
390         tio.c_lflag |= ISIG;
391         if (in_debugger) {
392                 tio.c_cc[VINTR] = init_tio.c_cc[VINTR];
393                 tio.c_cc[VSUSP] = init_tio.c_cc[VSUSP];
394                 tio.c_cc[VSTATUS] = init_tio.c_cc[VSTATUS];
395         } else {
396                 tio.c_cc[VINTR] = _POSIX_VDISABLE;
397                 tio.c_cc[VSUSP] = _POSIX_VDISABLE;
398                 tio.c_cc[VSTATUS] = _POSIX_VDISABLE;
399         }
400         tcsetattr(0, TCSAFLUSH, &tio);
401 }