Merge from vendor branch OPENSSH:
[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.12 2007/02/01 20:53:19 corecode 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      183
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         if (minor(dev) != 255)
100                 return(ENXIO);
101
102         tp = dev->si_tty = ttymalloc(dev->si_tty);
103         if ((tp->t_state & TS_ISOPEN) == 0) {
104                 tp->t_oproc = vcons_tty_start;
105                 tp->t_param = vcons_tty_param;
106                 tp->t_stop = nottystop;
107                 tp->t_dev = dev;
108
109                 tp->t_state |= TS_CARR_ON | TS_CONNECTED;
110                 ttychars(tp);
111                 tp->t_iflag = TTYDEF_IFLAG;
112                 tp->t_oflag = TTYDEF_OFLAG;
113                 tp->t_cflag = TTYDEF_CFLAG;
114                 tp->t_lflag = TTYDEF_LFLAG;
115                 tp->t_ispeed = TTYDEF_SPEED;
116                 tp->t_ospeed = TTYDEF_SPEED;
117                 ttsetwater(tp);
118         }
119         error = (*linesw[tp->t_line].l_open)(dev, tp);
120         ioctl(0, TIOCGWINSZ, &tp->t_winsize);
121
122         if (kqueue_console_info == NULL)
123                 kqueue_console_info = kqueue_add(0, vcons_intr, tp);
124         return(error);
125 }
126
127 static int
128 vcons_close(struct dev_close_args *ap)
129 {
130         cdev_t dev = ap->a_head.a_dev;
131         struct tty *tp;
132
133         if (minor(dev) != 255)
134                 return(ENXIO);
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         if (minor(dev) != 255)
149                 return(ENXIO);
150         tp = dev->si_tty;
151         error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
152                                               ap->a_fflag, ap->a_cred);
153         if (error != ENOIOCTL)
154                 return (error);
155         error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
156         if (error != ENOIOCTL)
157                 return (error);
158         return (ENOTTY);
159 }
160
161 static int
162 vcons_tty_param(struct tty *tp, struct termios *tio)
163 {
164         tp->t_ispeed = tio->c_ispeed;
165         tp->t_ospeed = tio->c_ospeed;
166         tp->t_cflag = tio->c_cflag;
167         return(0);
168 }
169
170 static void
171 vcons_tty_start(struct tty *tp)
172 {
173         int n;
174         char buf[64];
175
176         if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
177                 ttwwakeup(tp);
178                 return;
179         }
180         tp->t_state |= TS_BUSY;
181         while ((n = q_to_b(&tp->t_outq, buf, sizeof(buf))) > 0)
182                 write(1, buf, n);
183         tp->t_state &= ~TS_BUSY;
184         ttwwakeup(tp);
185 }
186
187 static
188 void
189 vcons_intr(void *tpx, struct intrframe *frame __unused)
190 {
191         struct tty *tp = tpx;
192         unsigned char buf[32];
193         int i;
194         int n;
195
196         /*
197          * If we aren't open we only have synchronous traffic via the
198          * debugger and do not need to poll.
199          */
200         if ((tp->t_state & TS_ISOPEN) == 0)
201                 return;
202
203         /*
204          * Only poll if we are open and haven't been stolen by the debugger.
205          */
206         if (console_stolen_by_kernel == 0 && (tp->t_state & TS_ISOPEN)) {
207                 do {
208                         n = extpread(0, buf, sizeof(buf), O_FNONBLOCKING, -1LL);
209                         for (i = 0; i < n; ++i)
210                                 (*linesw[tp->t_line].l_rint)(buf[i], tp);
211                 } while (n > 0);
212         }
213 }
214
215 /************************************************************************
216  *                      KERNEL CONSOLE INTERFACE                        *
217  ************************************************************************
218  *
219  * Kernel direct-call interface console driver
220  */
221 static cn_probe_t       vconsprobe;
222 static cn_init_t        vconsinit;
223 static cn_term_t        vconsterm;
224 static cn_getc_t        vconsgetc;
225 static cn_checkc_t      vconscheckc;
226 static cn_putc_t        vconsputc;
227
228 CONS_DRIVER(vcons, vconsprobe, vconsinit, vconsterm, vconsgetc, 
229                 vconscheckc, vconsputc, NULL);
230
231 static struct termios init_tio;
232 static struct consdev *vconsole;
233
234 static void
235 vconsprobe(struct consdev *cp)
236 {
237         cp->cn_pri = CN_NORMAL;
238         cp->cn_dev = make_dev(&vcons_ops, 255,
239                               UID_ROOT, GID_WHEEL, 0600, "vconsolectl");
240 }
241
242 /*
243  * This is a little bulky handler to set proper terminal
244  * settings in the case of a signal which might lead to
245  * termination or suspension.
246  */
247 static void
248 vconssignal(int sig)
249 {
250         struct termios curtio;
251         struct sigaction sa, osa;
252         sigset_t ss, oss;
253
254         tcgetattr(0, &curtio);
255         tcsetattr(0, TCSAFLUSH, &init_tio);
256         bzero(&sa, sizeof(sa));
257         sigemptyset(&sa.sa_mask);
258         sa.sa_handler = SIG_DFL;
259         sigaction(sig, &sa, &osa);
260         sigemptyset(&ss);
261         sigaddset(&ss, sig);
262         sigprocmask(SIG_UNBLOCK, &ss, &oss);
263         raise(sig);     /* now hand down the sig */
264         sigprocmask(SIG_SETMASK, &oss, NULL);
265         sigaction(sig, &osa, NULL);
266         tcsetattr(0, TCSAFLUSH, &curtio);
267 }
268
269 static void
270 vconswinch(int __unused sig)
271 {
272         struct winsize newsize;
273
274         if (vconsole != NULL && vconsole->cn_dev->si_tty != NULL) {
275                 ioctl(0, TIOCGWINSZ, &newsize);
276                 /*
277                  * ttioctl(vconsole->cn_dev->si_tty, TIOCSWINSZ, &newsize, 0);
278                  * I wished.  Unfortunately this needs a curproc, so do it
279                  * manually.
280                  */
281                 if (bcmp(&newsize, &vconsole->cn_dev->si_tty->t_winsize,
282                          sizeof(newsize)) != 0) {
283                         vconsole->cn_dev->si_tty->t_winsize = newsize;
284                         pgsignal(vconsole->cn_dev->si_tty->t_pgrp, SIGWINCH, 1);
285                 }
286         }
287 }
288
289 static void
290 vconscleanup(void)
291 {
292         tcsetattr(0, TCSAFLUSH, &init_tio);
293 }
294
295 static void
296 vconsinit(struct consdev *cp)
297 {
298         struct sigaction sa;
299
300         vconsole = cp;
301
302         tcgetattr(0, &init_tio);
303         bzero(&sa, sizeof(sa));
304         sigemptyset(&sa.sa_mask);
305         sa.sa_handler = vconssignal;
306         sigaction(SIGTSTP, &sa, NULL);
307         sigaction(SIGINT, &sa, NULL);
308         sigaction(SIGTERM, &sa, NULL);
309         atexit(vconscleanup);
310
311         sa.sa_handler = vconswinch;
312         sigaction(SIGWINCH, &sa, NULL);
313
314         vcons_set_mode(0);
315 }
316
317 static void
318 vconsterm(struct consdev *vp)
319 {
320         vconsole = NULL;
321         vconscleanup();
322 }
323
324 static int
325 vconsgetc(cdev_t dev)
326 {
327         unsigned char c;
328         ssize_t n;
329
330         console_stolen_by_kernel = 1;
331         for (;;) {
332                 if ((n = read(0, &c, 1)) == 1)
333                         break;
334                 if (n < 0 && errno == EINTR)
335                         continue;
336                 panic("vconsgetc: EOF on console %d %d", n ,errno);
337         }
338         console_stolen_by_kernel = 0;
339         return((int)c);
340 }
341
342 static int
343 vconscheckc(cdev_t dev)
344 {
345         unsigned char c;
346
347         if (extpread(0, &c, 1, O_FNONBLOCKING, -1LL) == 1)
348                 return((int)c);
349         return(-1);
350 }
351
352 static void
353 vconsputc(cdev_t dev, int c)
354 {
355         char cc = c;
356
357         write(1, &cc, 1);
358 }
359
360 void
361 vcons_set_mode(int in_debugger)
362 {
363         struct termios tio;
364
365         if (tcgetattr(0, &tio) < 0)
366                 return;
367         cfmakeraw(&tio);
368         tio.c_lflag |= ISIG;
369         if (in_debugger) {
370                 tio.c_cc[VINTR] = init_tio.c_cc[VINTR];
371                 tio.c_cc[VSUSP] = init_tio.c_cc[VSUSP];
372                 tio.c_cc[VSTATUS] = init_tio.c_cc[VSTATUS];
373         } else {
374                 tio.c_cc[VINTR] = _POSIX_VDISABLE;
375                 tio.c_cc[VSUSP] = _POSIX_VDISABLE;
376                 tio.c_cc[VSTATUS] = _POSIX_VDISABLE;
377         }
378         tcsetattr(0, TCSAFLUSH, &tio);
379 }