1 /* $OpenBSD: pcons.c,v 1.29 2024/05/14 08:26:13 jsg Exp $ */
2 /* $NetBSD: pcons.c,v 1.7 2001/05/02 10:32:20 scw Exp $ */
3
4 /*-
5 * Copyright (c) 2000 Eduardo E. Horvath
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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 the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Default console driver. Uses the PROM or whatever
34 * driver(s) are appropriate.
35 */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/conf.h>
40 #include <sys/device.h>
41 #include <sys/ioctl.h>
42 #include <sys/kernel.h>
43 #include <sys/proc.h>
44 #include <sys/tty.h>
45 #include <sys/time.h>
46 #include <sys/syslog.h>
47
48 #include <machine/autoconf.h>
49 #include <machine/openfirm.h>
50 #include <machine/conf.h>
51 #include <machine/cpu.h>
52 #include <machine/psl.h>
53
54 #include <dev/cons.h>
55
56 #include "wsdisplay.h"
57
58 #if NWSDISPLAY > 0
59 #include <dev/wscons/wsconsio.h>
60 #include <dev/wscons/wsdisplayvar.h>
61 #endif
62
63 struct pconssoftc {
64 struct device of_dev;
65
66 #if NWSDISPLAY > 0
67 int sc_wsdisplay;
68 u_int sc_nscreens;
69 #endif
70
71 struct tty *of_tty;
72 struct timeout sc_poll_to;
73 int of_flags;
74 };
75 /* flags: */
76 #define OFPOLL 1
77
78 #define OFBURSTLEN 128 /* max number of bytes to write in one chunk */
79
80 /* XXXXXXXX - this is in MI code in NetBSD */
81 /*
82 * Stuff to handle debugger magic key sequences.
83 */
84 #define CNS_LEN 128
85 #define CNS_MAGIC_VAL(x) ((x)&0x1ff)
86 #define CNS_MAGIC_NEXT(x) (((x)>>9)&0x7f)
87 #define CNS_TERM 0x7f /* End of sequence */
88
89 typedef struct cnm_state {
90 int cnm_state;
91 u_short *cnm_magic;
92 } cnm_state_t;
93 #ifdef DDB
94 #include <ddb/db_var.h>
95 #define cn_trap() do { if (db_console) db_enter(); } while (0)
96 #else
97 #define cn_trap()
98 #endif
99 #define cn_isconsole(d) ((d) == cn_tab->cn_dev)
100 void cn_init_magic(cnm_state_t *cnm);
101 void cn_destroy_magic(cnm_state_t *cnm);
102 int cn_set_magic(char *magic);
103 int cn_get_magic(char *magic, int len);
104 /* This should be called for each byte read */
105 #ifndef cn_check_magic
106 #define cn_check_magic(d, k, s) \
107 do { \
108 if (cn_isconsole(d)) { \
109 int v = (s).cnm_magic[(s).cnm_state]; \
110 if ((k) == CNS_MAGIC_VAL(v)) { \
111 (s).cnm_state = CNS_MAGIC_NEXT(v); \
112 if ((s).cnm_state == CNS_TERM) { \
113 cn_trap(); \
114 (s).cnm_state = 0; \
115 } \
116 } else { \
117 (s).cnm_state = 0; \
118 } \
119 } \
120 } while (/* CONSTCOND */ 0)
121 #endif
122
123 /* Encode out-of-band events this way when passing to cn_check_magic() */
124 #define CNC_BREAK 0x100
125
126 /* XXXXXXXXXX - end of this part of cnmagic, more at the end of this file. */
127
128 #include <sparc64/dev/cons.h>
129
130 int pconsmatch(struct device *, void *, void *);
131 void pconsattach(struct device *, struct device *, void *);
132
133 const struct cfattach pcons_ca = {
134 sizeof(struct pconssoftc), pconsmatch, pconsattach
135 };
136
137 struct cfdriver pcons_cd = {
138 NULL, "pcons", DV_TTY
139 };
140
141 extern struct cfdriver pcons_cd;
142 static struct cnm_state pcons_cnm_state;
143
144 static int pconsprobe(void);
145 static void pcons_wsdisplay_init(struct pconssoftc *);
146 extern struct consdev *cn_tab;
147
148 cons_decl(prom_);
149
150 int
pconsmatch(struct device * parent,void * match,void * aux)151 pconsmatch(struct device *parent, void *match, void *aux)
152 {
153 struct mainbus_attach_args *ma = aux;
154
155 /* Only attach if no other console has attached. */
156 return (strcmp("pcons", ma->ma_name) == 0 &&
157 cn_tab->cn_getc == prom_cngetc);
158 }
159
160 void pcons_poll(void *);
161
162 void
pconsattach(struct device * parent,struct device * self,void * aux)163 pconsattach(struct device *parent, struct device *self, void *aux)
164 {
165 struct pconssoftc *sc = (struct pconssoftc *) self;
166 #if NWSDISPLAY > 0
167 char buffer[128];
168 extern struct consdev wsdisplay_cons;
169 extern int wsdisplay_getc_dummy(dev_t);
170 #endif
171
172 printf("\n");
173 if (!pconsprobe())
174 return;
175
176 #if NWSDISPLAY > 0
177 /*
178 * Attach a dumb wsdisplay device if a wscons input driver has
179 * registered as the console, or is about to do so (usb keyboards).
180 */
181 if (wsdisplay_cons.cn_getc != wsdisplay_getc_dummy)
182 sc->sc_wsdisplay = 1;
183 else {
184 if (OF_getprop(OF_instance_to_package(stdin), "compatible",
185 buffer, sizeof(buffer)) != -1 &&
186 strncmp("usb", buffer, 3) == 0)
187 sc->sc_wsdisplay = 1;
188 }
189
190 if (sc->sc_wsdisplay != 0) {
191 pcons_wsdisplay_init(sc);
192 return;
193 }
194 #endif
195 cn_init_magic(&pcons_cnm_state);
196 cn_set_magic("+++++");
197 timeout_set(&sc->sc_poll_to, pcons_poll, sc);
198 }
199
200 void pconsstart(struct tty *);
201 int pconsparam(struct tty *, struct termios *);
202
203 int
pconsopen(dev_t dev,int flag,int mode,struct proc * p)204 pconsopen(dev_t dev, int flag, int mode, struct proc *p)
205 {
206 struct pconssoftc *sc;
207 int unit = minor(dev);
208 struct tty *tp;
209
210 if (unit >= pcons_cd.cd_ndevs)
211 return ENXIO;
212 sc = pcons_cd.cd_devs[unit];
213 if (!sc)
214 return ENXIO;
215 #if NWSDISPLAY > 0
216 if (sc->sc_wsdisplay != 0)
217 return ENXIO;
218 #endif
219 if (!(tp = sc->of_tty)) {
220 sc->of_tty = tp = ttymalloc(0);
221 }
222 tp->t_oproc = pconsstart;
223 tp->t_param = pconsparam;
224 tp->t_dev = dev;
225 cn_tab->cn_dev = dev;
226 if (!(tp->t_state & TS_ISOPEN)) {
227 ttychars(tp);
228 tp->t_iflag = TTYDEF_IFLAG;
229 tp->t_oflag = TTYDEF_OFLAG;
230 tp->t_cflag = TTYDEF_CFLAG;
231 tp->t_lflag = TTYDEF_LFLAG;
232 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
233 pconsparam(tp, &tp->t_termios);
234 ttsetwater(tp);
235 } else if ((tp->t_state & TS_XCLUDE) && suser(p))
236 return EBUSY;
237 tp->t_state |= TS_CARR_ON;
238
239 if (!(sc->of_flags & OFPOLL)) {
240 sc->of_flags |= OFPOLL;
241 timeout_add(&sc->sc_poll_to, 1);
242 }
243
244 return (*linesw[tp->t_line].l_open)(dev, tp, p);
245 }
246
247 int
pconsclose(dev_t dev,int flag,int mode,struct proc * p)248 pconsclose(dev_t dev, int flag, int mode, struct proc *p)
249 {
250 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
251 struct tty *tp = sc->of_tty;
252
253 timeout_del(&sc->sc_poll_to);
254 sc->of_flags &= ~OFPOLL;
255 (*linesw[tp->t_line].l_close)(tp, flag, p);
256 ttyclose(tp);
257 return 0;
258 }
259
260 int
pconsread(dev_t dev,struct uio * uio,int flag)261 pconsread(dev_t dev, struct uio *uio, int flag)
262 {
263 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
264 struct tty *tp = sc->of_tty;
265
266 return (*linesw[tp->t_line].l_read)(tp, uio, flag);
267 }
268
269 int
pconswrite(dev_t dev,struct uio * uio,int flag)270 pconswrite(dev_t dev, struct uio *uio, int flag)
271 {
272 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
273 struct tty *tp = sc->of_tty;
274
275 return (*linesw[tp->t_line].l_write)(tp, uio, flag);
276 }
277
278 int
pconsioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)279 pconsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
280 {
281 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
282 struct tty *tp = sc->of_tty;
283 int error;
284
285 if ((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p)) >= 0)
286 return error;
287 if ((error = ttioctl(tp, cmd, data, flag, p)) >= 0)
288 return error;
289 return ENOTTY;
290 }
291
292 struct tty *
pconstty(dev_t dev)293 pconstty(dev_t dev)
294 {
295 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)];
296
297 return sc->of_tty;
298 }
299
300 int
pconsstop(struct tty * tp,int flag)301 pconsstop(struct tty *tp, int flag)
302 {
303 return 0;
304 }
305
306 void
pconsstart(struct tty * tp)307 pconsstart(struct tty *tp)
308 {
309 struct clist *cl;
310 int s, len;
311 u_char buf[OFBURSTLEN];
312
313 s = spltty();
314 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
315 splx(s);
316 return;
317 }
318 tp->t_state |= TS_BUSY;
319 splx(s);
320 cl = &tp->t_outq;
321 len = q_to_b(cl, buf, OFBURSTLEN);
322 OF_write(stdout, buf, len);
323 s = spltty();
324 tp->t_state &= ~TS_BUSY;
325 if (cl->c_cc) {
326 tp->t_state |= TS_TIMEOUT;
327 timeout_add(&tp->t_rstrt_to, 1);
328 }
329 if (cl->c_cc <= tp->t_lowat) {
330 if (tp->t_state & TS_ASLEEP) {
331 tp->t_state &= ~TS_ASLEEP;
332 wakeup(cl);
333 }
334 selwakeup(&tp->t_wsel);
335 }
336 splx(s);
337 }
338
339 int
pconsparam(struct tty * tp,struct termios * t)340 pconsparam(struct tty *tp, struct termios *t)
341 {
342 tp->t_ispeed = t->c_ispeed;
343 tp->t_ospeed = t->c_ospeed;
344 tp->t_cflag = t->c_cflag;
345 return 0;
346 }
347
348 void
pcons_poll(void * aux)349 pcons_poll(void *aux)
350 {
351 struct pconssoftc *sc = aux;
352 struct tty *tp = sc->of_tty;
353 char ch;
354
355 while (OF_read(stdin, &ch, 1) > 0) {
356 cn_check_magic(tp->t_dev, ch, pcons_cnm_state);
357 if (tp && (tp->t_state & TS_ISOPEN)) {
358 if (ch == '\b')
359 ch = '\177';
360 (*linesw[tp->t_line].l_rint)(ch, tp);
361 }
362 }
363 timeout_add(&sc->sc_poll_to, 1);
364 }
365
366 int
pconsprobe(void)367 pconsprobe(void)
368 {
369 if (!stdin) stdin = OF_stdin();
370 if (!stdout) stdout = OF_stdout();
371
372 return (stdin && stdout);
373 }
374
375 void
pcons_cnpollc(dev_t dev,int on)376 pcons_cnpollc(dev_t dev, int on)
377 {
378 struct pconssoftc *sc = NULL;
379
380 if (pcons_cd.cd_devs)
381 sc = pcons_cd.cd_devs[minor(dev)];
382
383 if (sc == NULL)
384 return;
385
386 if (on) {
387 if (sc->of_flags & OFPOLL)
388 timeout_del(&sc->sc_poll_to);
389 sc->of_flags &= ~OFPOLL;
390 } else {
391 /* Resuming kernel. */
392 if (!(sc->of_flags & OFPOLL)) {
393 sc->of_flags |= OFPOLL;
394 timeout_add(&sc->sc_poll_to, 1);
395 }
396 }
397 }
398
399 /* XXXXXXXX --- more cnmagic stuff. */
400 #define ENCODE_STATE(c, n) (short)(((c)&0x1ff)|(((n)&0x7f)<<9))
401
402 static unsigned short cn_magic[CNS_LEN];
403
404 /*
405 * Initialize a cnm_state_t.
406 */
407 void
cn_init_magic(cnm_state_t * cnm)408 cn_init_magic(cnm_state_t *cnm)
409 {
410 cnm->cnm_state = 0;
411 cnm->cnm_magic = cn_magic;
412 }
413
414 /*
415 * Destroy a cnm_state_t.
416 */
417 void
cn_destroy_magic(cnm_state_t * cnm)418 cn_destroy_magic(cnm_state_t *cnm)
419 {
420 cnm->cnm_state = 0;
421 cnm->cnm_magic = NULL;
422 }
423
424 /*
425 * Translate a magic string to a state
426 * machine table.
427 */
428 int
cn_set_magic(char * magic)429 cn_set_magic(char *magic)
430 {
431 unsigned int i, c, n;
432 unsigned short m[CNS_LEN];
433
434 for (i=0; i<CNS_LEN; i++) {
435 c = (*magic++)&0xff;
436 n = *magic ? i+1 : CNS_TERM;
437 switch (c) {
438 case 0:
439 /* End of string */
440 if (i == 0) {
441 /* empty string? */
442 cn_magic[0] = 0;
443 #ifdef DEBUG
444 printf("cn_set_magic(): empty!\n");
445 #endif
446 return (0);
447 }
448 do {
449 cn_magic[i] = m[i];
450 } while (i--);
451 return(0);
452 case 0x27:
453 /* Escape sequence */
454 c = (*magic++)&0xff;
455 n = *magic ? i+1 : CNS_TERM;
456 switch (c) {
457 case 0x27:
458 break;
459 case 0x01:
460 /* BREAK */
461 c = CNC_BREAK;
462 break;
463 case 0x02:
464 /* NUL */
465 c = 0;
466 break;
467 }
468 /* FALLTHROUGH */
469 default:
470 /* Transition to the next state. */
471 #ifdef DEBUG
472 if (!cold)
473 printf("mag %d %x:%x\n", i, c, n);
474 #endif
475 m[i] = ENCODE_STATE(c, n);
476 break;
477 }
478 }
479 return (EINVAL);
480 }
481
482 /*
483 * Translate a state machine table back to
484 * a magic string.
485 */
486 int
cn_get_magic(char * magic,int maglen)487 cn_get_magic(char *magic, int maglen) {
488 unsigned int i, c;
489
490 for (i=0; i<CNS_LEN; i++) {
491 c = cn_magic[i];
492 /* Translate a character */
493 switch (CNS_MAGIC_VAL(c)) {
494 case CNC_BREAK:
495 *magic++ = 0x27;
496 *magic++ = 0x01;
497 break;
498 case 0:
499 *magic++ = 0x27;
500 *magic++ = 0x02;
501 break;
502 case 0x27:
503 *magic++ = 0x27;
504 *magic++ = 0x27;
505 break;
506 default:
507 *magic++ = (c&0x0ff);
508 break;
509 }
510 /* Now go to the next state */
511 i = CNS_MAGIC_NEXT(c);
512 if (i == CNS_TERM || i == 0) {
513 /* Either termination state or empty machine */
514 *magic++ = 0;
515 return (0);
516 }
517 }
518 return (EINVAL);
519 }
520
521 #if NWSDISPLAY > 0
522
523 int pcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
524 int *, int *, uint32_t *);
525 void pcons_free_screen(void *, void *);
526 int pcons_ioctl(void *, u_long, caddr_t, int, struct proc *);
527 int pcons_mapchar(void *, int, unsigned int *);
528 paddr_t pcons_mmap(void *, off_t, int);
529 int pcons_putchar(void *, int, int, u_int, uint32_t);
530 int pcons_show_screen(void *, void *, int, void (*)(void *, int, int),
531 void *);
532
533 struct wsdisplay_emulops pcons_emulops = {
534 NULL,
535 pcons_mapchar,
536 pcons_putchar
537 };
538
539 struct wsscreen_descr pcons_stdscreen = {
540 "dumb", 80, 34, &pcons_emulops, 12, 22, 0
541 };
542
543 const struct wsscreen_descr *pcons_scrlist[] = {
544 &pcons_stdscreen
545 };
546
547 struct wsscreen_list pcons_screenlist = {
548 1, pcons_scrlist
549 };
550
551 struct wsdisplay_accessops pcons_accessops = {
552 .ioctl = pcons_ioctl,
553 .mmap = pcons_mmap,
554 .alloc_screen = pcons_alloc_screen,
555 .free_screen = pcons_free_screen,
556 .show_screen = pcons_show_screen
557 };
558
559 int
pcons_alloc_screen(void * v,const struct wsscreen_descr * typ,void ** cookiep,int * curxp,int * curyp,uint32_t * attrp)560 pcons_alloc_screen(void *v, const struct wsscreen_descr *typ, void **cookiep,
561 int *curxp, int *curyp, uint32_t *attrp)
562 {
563 struct pconssoftc *sc = v;
564 int *rowp, *colp;
565 int row, col;
566
567 if (sc->sc_nscreens > 0)
568 return (ENOMEM);
569
570 row = col = 0;
571 if (romgetcursoraddr(&rowp, &colp) == 0) {
572 if (rowp != NULL)
573 row = *rowp;
574 if (colp != NULL)
575 col = *colp;
576 }
577
578 *cookiep = v;
579 *attrp = 0;
580 *curxp = col;
581 *curyp = row;
582
583 sc->sc_nscreens++;
584 return (0);
585 }
586
587 void
pcons_free_screen(void * v,void * cookie)588 pcons_free_screen(void *v, void *cookie)
589 {
590 struct pconssoftc *sc = v;
591
592 sc->sc_nscreens--;
593 }
594
595 int
pcons_ioctl(void * v,u_long cmd,caddr_t data,int flags,struct proc * p)596 pcons_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
597 {
598 switch (cmd) {
599 case WSDISPLAYIO_GTYPE:
600 *(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
601 break;
602 default:
603 return (-1);
604 }
605
606 return (0);
607 }
608
609 paddr_t
pcons_mmap(void * v,off_t off,int prot)610 pcons_mmap(void *v, off_t off, int prot)
611 {
612 return ((paddr_t)-1);
613 }
614
615 int
pcons_show_screen(void * v,void * cookie,int waitok,void (* cb)(void *,int,int),void * arg)616 pcons_show_screen(void *v, void *cookie, int waitok,
617 void (*cb)(void *, int, int), void *arg)
618 {
619 return (0);
620 }
621
622 int
pcons_mapchar(void * v,int uc,unsigned int * idx)623 pcons_mapchar(void *v, int uc, unsigned int *idx)
624 {
625 if ((uc & 0xff) == uc) {
626 *idx = uc;
627 return (1);
628 } else {
629 *idx = '?';
630 return (0);
631 }
632 }
633
634 int
pcons_putchar(void * v,int row,int col,u_int uc,uint32_t attr)635 pcons_putchar(void *v, int row, int col, u_int uc, uint32_t attr)
636 {
637 u_char buf[1];
638 int s;
639
640 buf[0] = (u_char)uc;
641 s = splhigh();
642 OF_write(stdout, &buf, 1);
643 splx(s);
644
645 return 0;
646 }
647
648 void
pcons_wsdisplay_init(struct pconssoftc * sc)649 pcons_wsdisplay_init(struct pconssoftc *sc)
650 {
651 struct wsemuldisplaydev_attach_args waa;
652 int *rowp, *colp;
653 int options, row, col;
654
655 row = col = 0;
656 if (romgetcursoraddr(&rowp, &colp) == 0) {
657 if (rowp != NULL)
658 row = *rowp;
659 if (colp != NULL)
660 col = *colp;
661 }
662
663 options = OF_finddevice("/options");
664 pcons_stdscreen.nrows = getpropint(options, "screen-#rows", 34);
665 pcons_stdscreen.ncols = getpropint(options, "screen-#columns", 80);
666
667 /*
668 * We claim console here, because we can only get there if stdin
669 * is a keyboard. However, the PROM could have been configured with
670 * stdin being a keyboard and stdout being a serial sink.
671 * But since this combination is not supported under OpenBSD at the
672 * moment, it is reasonably safe to attach a dumb display as console
673 * here.
674 */
675 wsdisplay_cnattach(&pcons_stdscreen, sc, col, row, 0);
676
677 waa.console = 1;
678 waa.scrdata = &pcons_screenlist;
679 waa.accessops = &pcons_accessops;
680 waa.accesscookie = sc;
681 waa.defaultscreens = 1;
682
683 config_found((struct device *)sc, &waa, wsemuldisplaydevprint);
684 }
685 #endif
686