kd.c revision 1.56.14.1 1 /* $NetBSD: kd.c,v 1.56.14.1 2012/11/20 03:01:47 tls Exp $ */
2
3 /*-
4 * Copyright (c) 1996 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Gordon W. Ross.
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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Keyboard/Display device.
34 *
35 * This driver exists simply to provide a tty device that
36 * the indirect console driver can point to.
37 * The kbd driver sends its input here.
38 * Output goes to the screen via PROM printf.
39 */
40
41 #include <sys/cdefs.h>
42 __KERNEL_RCSID(0, "$NetBSD: kd.c,v 1.56.14.1 2012/11/20 03:01:47 tls Exp $");
43
44 #include <sys/param.h>
45 #include <sys/proc.h>
46 #include <sys/systm.h>
47 #include <sys/ioctl.h>
48 #include <sys/tty.h>
49 #include <sys/file.h>
50 #include <sys/conf.h>
51 #include <sys/device.h>
52 #include <sys/kauth.h>
53
54 #include <machine/autoconf.h>
55 #include <machine/cpu.h>
56 #include <machine/mon.h>
57 #include <machine/psl.h>
58
59 #include <dev/cons.h>
60 #include <dev/sun/event_var.h>
61 #include <dev/sun/kbd_xlate.h>
62 #include <dev/sun/kbdvar.h>
63 #include <sun3/dev/zs_cons.h>
64
65 #include "fb.h"
66
67 #define PUT_WSIZE 64
68
69 struct kd_softc {
70 struct tty *kd_tty;
71
72 /* Console input hook */
73 struct cons_channel *kd_in;
74 };
75
76 /*
77 * There is no point in pretending there might be
78 * more than one keyboard/display device.
79 */
80 static struct kd_softc kd_softc;
81 static int kd_is_console;
82
83 static int kdparam(struct tty *, struct termios *);
84 static void kdstart(struct tty *);
85 static void kd_init(struct kd_softc *);
86 static void kd_cons_input(int);
87 static void kd_later(void *);
88
89 dev_type_open(kdopen);
90 dev_type_close(kdclose);
91 dev_type_read(kdread);
92 dev_type_write(kdwrite);
93 dev_type_ioctl(kdioctl);
94 dev_type_tty(kdtty);
95 dev_type_poll(kdpoll);
96
97 const struct cdevsw kd_cdevsw = {
98 kdopen, kdclose, kdread, kdwrite, kdioctl,
99 nostop, kdtty, kdpoll, nommap, ttykqfilter, D_TTY
100 };
101
102 /*
103 * Prepare the console tty; called on first open of /dev/console
104 */
105 void
106 kd_init(struct kd_softc *kd)
107 {
108 struct tty *tp;
109
110 tp = tty_alloc();
111 callout_setfunc(&tp->t_rstrt_ch, kd_later, tp);
112
113 tp->t_oproc = kdstart;
114 tp->t_param = kdparam;
115 tp->t_dev = makedev(cdevsw_lookup_major(&kd_cdevsw), 0);
116
117 tty_attach(tp);
118 kd->kd_tty = tp;
119
120 return;
121 }
122
123 struct tty *
124 kdtty(dev_t dev)
125 {
126 struct kd_softc *kd;
127
128 kd = &kd_softc; /* XXX */
129 return (kd->kd_tty);
130 }
131
132 int
133 kdopen(dev_t dev, int flag, int mode, struct lwp *l)
134 {
135 struct kd_softc *kd;
136 int error, s, unit;
137 struct tty *tp;
138 static int firstopen = 1;
139
140 unit = minor(dev);
141 if (unit != 0)
142 return ENXIO;
143 kd = &kd_softc; /* XXX */
144 if (firstopen) {
145 kd_init(kd);
146 firstopen = 0;
147 }
148 tp = kd->kd_tty;
149
150 /* It's simpler to do this up here. */
151 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
152 return (EBUSY);
153
154 s = spltty();
155
156 if ((tp->t_state & TS_ISOPEN) == 0) {
157 /* First open. */
158
159 /* Notify the input device that serves us */
160 struct cons_channel *cc = kd->kd_in;
161 if (cc != NULL &&
162 (error = (*cc->cc_iopen)(cc)) != 0) {
163 return (error);
164 }
165
166 ttychars(tp);
167 tp->t_iflag = TTYDEF_IFLAG;
168 tp->t_oflag = TTYDEF_OFLAG;
169 tp->t_cflag = TTYDEF_CFLAG;
170 tp->t_lflag = TTYDEF_LFLAG;
171 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
172 (void) kdparam(tp, &tp->t_termios);
173 ttsetwater(tp);
174 /* Flush pending input? Clear translator? */
175 /* This (pseudo)device always has SOFTCAR */
176 tp->t_state |= TS_CARR_ON;
177 }
178
179 splx(s);
180
181 return ((*tp->t_linesw->l_open)(dev, tp));
182 }
183
184 int
185 kdclose(dev_t dev, int flag, int mode, struct lwp *l)
186 {
187 struct kd_softc *kd;
188 struct tty *tp;
189 struct cons_channel *cc;
190
191 kd = &kd_softc; /* XXX */
192 tp = kd->kd_tty;
193
194 /* XXX This is for cons.c. */
195 if ((tp->t_state & TS_ISOPEN) == 0)
196 return 0;
197
198 (*tp->t_linesw->l_close)(tp, flag);
199 ttyclose(tp);
200 if ((cc = kd->kd_in) != NULL)
201 (void)(*cc->cc_iclose)(cc);
202 return (0);
203 }
204
205 int
206 kdread(dev_t dev, struct uio *uio, int flag)
207 {
208 struct kd_softc *kd;
209 struct tty *tp;
210
211 kd = &kd_softc; /* XXX */
212 tp = kd->kd_tty;
213
214 return ((*tp->t_linesw->l_read)(tp, uio, flag));
215 }
216
217 int
218 kdwrite(dev_t dev, struct uio *uio, int flag)
219 {
220 struct kd_softc *kd;
221 struct tty *tp;
222
223 kd = &kd_softc; /* XXX */
224 tp = kd->kd_tty;
225
226 return ((*tp->t_linesw->l_write)(tp, uio, flag));
227 }
228
229 int
230 kdpoll(dev_t dev, int events, struct lwp *l)
231 {
232 struct kd_softc *kd;
233 struct tty *tp;
234
235 kd = &kd_softc; /* XXX */
236 tp = kd->kd_tty;
237
238 return ((*tp->t_linesw->l_poll)(tp, events, l));
239 }
240
241 int
242 kdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
243 {
244 struct kd_softc *kd;
245 struct tty *tp;
246 int error;
247
248 kd = &kd_softc; /* XXX */
249 tp = kd->kd_tty;
250
251 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
252 if (error != EPASSTHROUGH)
253 return error;
254
255 error = ttioctl(tp, cmd, data, flag, l);
256 if (error != EPASSTHROUGH)
257 return error;
258
259 /* Handle any ioctl commands specific to kbd/display. */
260 /* XXX - Send KB* ioctls to kbd module? */
261 /* XXX - Send FB* ioctls to fb module? */
262
263 return EPASSTHROUGH;
264 }
265
266 static int
267 kdparam(struct tty *tp, struct termios *t)
268 {
269 /* XXX - These are ignored... */
270 tp->t_ispeed = t->c_ispeed;
271 tp->t_ospeed = t->c_ospeed;
272 tp->t_cflag = t->c_cflag;
273 return 0;
274 }
275
276
277 static void kd_putfb(struct tty *);
278
279 static void
280 kdstart(struct tty *tp)
281 {
282 struct clist *cl;
283 int s1, s2;
284
285 s1 = splsoftclock();
286 s2 = spltty();
287 if (tp->t_state & (TS_BUSY|TS_TTSTOP|TS_TIMEOUT))
288 goto out;
289
290 cl = &tp->t_outq;
291 if (ttypull(tp)) {
292 if (kd_is_console) {
293 tp->t_state |= TS_BUSY;
294 if ((s1 & PSL_IPL) == 0) {
295 /* called at level zero - update screen now. */
296 splx(s2);
297 kd_putfb(tp);
298 s2 = spltty();
299 tp->t_state &= ~TS_BUSY;
300 } else {
301 /* called at interrupt level - do it later */
302 callout_schedule(&tp->t_rstrt_ch, 0);
303 }
304 } else {
305 /*
306 * This driver uses the PROM for writing the screen,
307 * and that only works if this is the console device.
308 * If this is not the console, just flush the output.
309 * Sorry. (In that case, use xdm instead of getty.)
310 */
311 ndflush(cl, cl->c_cc);
312 }
313 }
314 out:
315 splx(s2);
316 splx(s1);
317 }
318
319 /*
320 * Timeout function to do delayed writes to the screen.
321 * Called at splsoftclock when requested by kdstart.
322 */
323 static void
324 kd_later(void *tpaddr)
325 {
326 struct tty *tp = tpaddr;
327 int s;
328
329 kd_putfb(tp);
330
331 s = spltty();
332 tp->t_state &= ~TS_BUSY;
333 (*tp->t_linesw->l_start)(tp);
334 splx(s);
335 }
336
337 /*
338 * Put text on the screen using the PROM monitor.
339 * This can take a while, so to avoid missing
340 * interrupts, this is called at splsoftclock.
341 */
342 static void
343 kd_putfb(struct tty *tp)
344 {
345 char buf[PUT_WSIZE];
346 struct clist *cl = &tp->t_outq;
347 char *p, *end;
348 int len;
349
350 while ((len = q_to_b(cl, buf, PUT_WSIZE-1)) > 0) {
351 /* PROM will barf if high bits are set. */
352 p = buf;
353 end = buf + len;
354 while (p < end)
355 *p++ &= 0x7f;
356 (romVectorPtr->fbWriteStr)(buf, len);
357 }
358 }
359
360 void
361 cons_attach_input(struct cons_channel *cc, struct consdev *cn)
362 {
363 struct kd_softc *kd = &kd_softc;
364
365 kd->kd_in = cc;
366 cc->cc_upstream = kd_cons_input;
367 }
368
369 /*
370 * Default PROM-based console input stream
371 */
372 static int kd_rom_iopen(struct cons_channel *);
373 static int kd_rom_iclose(struct cons_channel *);
374
375 static struct cons_channel prom_cons_channel;
376
377 int
378 kd_rom_iopen(struct cons_channel *cc)
379 {
380 /* No-op */
381 return (0);
382 }
383
384 int
385 kd_rom_iclose(struct cons_channel *cc)
386 {
387 /* No-op */
388 return (0);
389 }
390
391 /*
392 * Our "interrupt" routine for input. This is called by
393 * the keyboard driver (dev/sun/kbd.c) at spltty.
394 */
395 void
396 kd_cons_input(int c)
397 {
398 struct kd_softc *kd = &kd_softc;
399 struct tty *tp;
400
401 /* XXX: Make sure the device is open. */
402 tp = kd->kd_tty;
403 if (tp == NULL)
404 return;
405 if ((tp->t_state & TS_ISOPEN) == 0)
406 return;
407
408 (*tp->t_linesw->l_rint)(c, tp);
409 }
410
411
412 /****************************************************************
413 * kd console support
414 ****************************************************************/
415
416 /* The debugger gets its own key translation state. */
417 static struct kbd_state kdcn_state;
418
419 static void kdcnprobe(struct consdev *);
420 static void kdcninit(struct consdev *);
421 static int kdcngetc(dev_t);
422 static void kdcnputc(dev_t, int);
423 static void kdcnpollc(dev_t, int);
424
425 struct consdev consdev_kd = {
426 kdcnprobe,
427 kdcninit,
428 kdcngetc,
429 kdcnputc,
430 kdcnpollc,
431 NULL,
432 };
433
434 /* We never call this. */
435 static void
436 kdcnprobe(struct consdev *cn)
437 {
438 }
439
440 static void
441 kdcninit(struct consdev *cn)
442 {
443 struct kbd_state *ks = &kdcn_state;
444
445 cn->cn_dev = makedev(cdevsw_lookup_major(&kd_cdevsw), 0);
446 cn->cn_pri = CN_INTERNAL;
447
448 /* This prepares kbd_translate() */
449 ks->kbd_id = KBD_MIN_TYPE;
450 kbd_xlate_init(ks);
451
452 /* Set up initial PROM input channel for /dev/console */
453 prom_cons_channel.cc_private = NULL;
454 prom_cons_channel.cc_iopen = kd_rom_iopen;
455 prom_cons_channel.cc_iclose = kd_rom_iclose;
456 cons_attach_input(&prom_cons_channel, cn);
457
458 /* Indicate that it is OK to use the PROM fbwrite */
459 kd_is_console = 1;
460 }
461
462 static int
463 kdcngetc(dev_t dev)
464 {
465 struct kbd_state *ks = &kdcn_state;
466 int code, class, data, keysym;
467
468 for (;;) {
469 code = zs_getc(zs_conschan);
470 keysym = kbd_code_to_keysym(ks, code);
471 class = KEYSYM_CLASS(keysym);
472
473 switch (class) {
474 case KEYSYM_ASCII:
475 goto out;
476
477 case KEYSYM_CLRMOD:
478 case KEYSYM_SETMOD:
479 data = (keysym & 0x1F);
480 /* Only allow ctrl or shift. */
481 if (data > KBMOD_SHIFT_R)
482 break;
483 data = 1 << data;
484 if (class == KEYSYM_SETMOD)
485 ks->kbd_modbits |= data;
486 else
487 ks->kbd_modbits &= ~data;
488 break;
489
490 case KEYSYM_ALL_UP:
491 /* No toggle keys here. */
492 ks->kbd_modbits = 0;
493 break;
494
495 default: /* ignore all other keysyms */
496 break;
497 }
498 }
499 out:
500 return (keysym);
501 }
502
503 static void
504 kdcnputc(dev_t dev, int c)
505 {
506 (romVectorPtr->fbWriteChar)(c & 0x7f);
507 }
508
509 static void
510 kdcnpollc(dev_t dev, int on)
511 {
512 struct kbd_state *ks = &kdcn_state;
513
514 if (on) {
515 /* Entering debugger. */
516 #if NFB > 0
517 fb_unblank();
518 #endif
519 /* Clear shift keys too. */
520 ks->kbd_modbits = 0;
521 } else {
522 /* Resuming kernel. */
523 }
524 }
525
526