FreeBSD/Linux Kernel Cross Reference
sys/dev/dcons/dcons.c
1 /*
2 * Copyright (C) 2003
3 * Hidetoshi Shimokawa. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 *
16 * This product includes software developed by Hidetoshi Shimokawa.
17 *
18 * 4. Neither the name of the author nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $Id: dcons.c,v 1.65 2003/10/24 03:24:55 simokawa Exp $
35 * $FreeBSD: releng/5.3/sys/dev/dcons/dcons.c 132226 2004-07-15 20:47:41Z phk $
36 */
37
38 #include <sys/param.h>
39 #if __FreeBSD_version >= 502122
40 #include <sys/kdb.h>
41 #include <gdb/gdb.h>
42 #endif
43 #include <sys/kernel.h>
44 #include <sys/module.h>
45 #include <sys/systm.h>
46 #include <sys/types.h>
47 #include <sys/conf.h>
48 #include <sys/cons.h>
49 #include <sys/consio.h>
50 #include <sys/tty.h>
51 #include <sys/malloc.h>
52 #include <sys/proc.h>
53 #include <sys/ucred.h>
54
55 #include <machine/bus.h>
56
57 #include <dev/dcons/dcons.h>
58
59 #include <ddb/ddb.h>
60 #include <sys/reboot.h>
61
62 #include <sys/sysctl.h>
63
64 #include "opt_ddb.h"
65 #include "opt_comconsole.h"
66 #include "opt_dcons.h"
67
68 #ifndef DCONS_POLL_HZ
69 #define DCONS_POLL_HZ 100
70 #endif
71
72 #ifndef DCONS_BUF_SIZE
73 #define DCONS_BUF_SIZE (16*1024)
74 #endif
75
76 #ifndef DCONS_FORCE_CONSOLE
77 #define DCONS_FORCE_CONSOLE 0 /* mostly for FreeBSD-4 */
78 #endif
79
80 #ifndef DCONS_FORCE_GDB
81 #define DCONS_FORCE_GDB 1
82 #endif
83
84 #if __FreeBSD_version >= 500101
85 #define CONS_NODEV 1
86 #if __FreeBSD_version < 502122
87 static struct consdev gdbconsdev;
88 #endif
89 #endif
90
91
92 static d_open_t dcons_open;
93 static d_close_t dcons_close;
94
95 static struct cdevsw dcons_cdevsw = {
96 #if __FreeBSD_version >= 500104
97 .d_version = D_VERSION,
98 .d_open = dcons_open,
99 .d_close = dcons_close,
100 .d_name = "dcons",
101 .d_flags = D_TTY | D_NEEDGIANT,
102 #else
103 /* open */ dcons_open,
104 /* close */ dcons_close,
105 /* read */ ttyread,
106 /* write */ ttywrite,
107 /* ioctl */ dcons_ioctl,
108 /* poll */ ttypoll,
109 /* mmap */ nommap,
110 /* strategy */ nostrategy,
111 /* name */ "dcons",
112 /* major */ CDEV_MAJOR,
113 /* dump */ nodump,
114 /* psize */ nopsize,
115 /* flags */ 0,
116 #endif
117 };
118
119 #ifndef KLD_MODULE
120 static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */
121 #endif
122
123 /* global data */
124 static struct dcons_global dg;
125 struct dcons_global *dcons_conf;
126 static int poll_hz = DCONS_POLL_HZ;
127
128 SYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console");
129 SYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0,
130 "dcons polling rate");
131
132 static int drv_init = 0;
133 static struct callout dcons_callout;
134 struct dcons_buf *dcons_buf; /* for local dconschat */
135
136 /* per device data */
137 static struct dcons_softc {
138 struct cdev *dev;
139 struct dcons_ch o, i;
140 int brk_state;
141 #define DC_GDB 1
142 int flags;
143 } sc[DCONS_NPORT];
144 static void dcons_tty_start(struct tty *);
145 static int dcons_tty_param(struct tty *, struct termios *);
146 static void dcons_timeout(void *);
147 static int dcons_drv_init(int);
148 static int dcons_getc(struct dcons_softc *);
149 static int dcons_checkc(struct dcons_softc *);
150 static void dcons_putc(struct dcons_softc *, int);
151
152 static cn_probe_t dcons_cnprobe;
153 static cn_init_t dcons_cninit;
154 static cn_getc_t dcons_cngetc;
155 static cn_checkc_t dcons_cncheckc;
156 static cn_putc_t dcons_cnputc;
157
158 CONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, NULL, dcons_cngetc,
159 dcons_cncheckc, dcons_cnputc, NULL);
160
161 #if __FreeBSD_version >= 502122
162 static gdb_probe_f dcons_dbg_probe;
163 static gdb_init_f dcons_dbg_init;
164 static gdb_term_f dcons_dbg_term;
165 static gdb_getc_f dcons_dbg_getc;
166 static gdb_checkc_f dcons_dbg_checkc;
167 static gdb_putc_f dcons_dbg_putc;
168
169 GDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term,
170 dcons_dbg_checkc, dcons_dbg_getc, dcons_dbg_putc);
171
172 extern struct gdb_dbgport *gdb_cur;
173 #endif
174
175 #if __FreeBSD_version < 500000
176 #define THREAD proc
177 #else
178 #define THREAD thread
179 #endif
180
181 static int
182 dcons_open(struct cdev *dev, int flag, int mode, struct THREAD *td)
183 {
184 struct tty *tp;
185 int unit, error, s;
186
187 unit = minor(dev);
188 if (unit != 0)
189 return (ENXIO);
190
191 tp = dev->si_tty = ttymalloc(dev->si_tty);
192 tp->t_oproc = dcons_tty_start;
193 tp->t_param = dcons_tty_param;
194 tp->t_stop = nottystop;
195 tp->t_dev = dev;
196
197 error = 0;
198
199 s = spltty();
200 if ((tp->t_state & TS_ISOPEN) == 0) {
201 tp->t_state |= TS_CARR_ON;
202 ttychars(tp);
203 tp->t_iflag = TTYDEF_IFLAG;
204 tp->t_oflag = TTYDEF_OFLAG;
205 tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
206 tp->t_lflag = TTYDEF_LFLAG;
207 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
208 ttsetwater(tp);
209 } else if ((tp->t_state & TS_XCLUDE) && suser(td)) {
210 splx(s);
211 return (EBUSY);
212 }
213 splx(s);
214
215 error = ttyld_open(tp, dev);
216
217 return (error);
218 }
219
220 static int
221 dcons_close(struct cdev *dev, int flag, int mode, struct THREAD *td)
222 {
223 int unit;
224 struct tty *tp;
225
226 unit = minor(dev);
227 if (unit != 0)
228 return (ENXIO);
229
230 tp = dev->si_tty;
231 if (tp->t_state & TS_ISOPEN) {
232 ttyld_close(tp, flag);
233 tty_close(tp);
234 }
235
236 return (0);
237 }
238
239 static int
240 dcons_tty_param(struct tty *tp, struct termios *t)
241 {
242 tp->t_ispeed = t->c_ispeed;
243 tp->t_ospeed = t->c_ospeed;
244 tp->t_cflag = t->c_cflag;
245 return 0;
246 }
247
248 static void
249 dcons_tty_start(struct tty *tp)
250 {
251 struct dcons_softc *dc;
252 int s;
253
254 dc = (struct dcons_softc *)tp->t_dev->si_drv1;
255 s = spltty();
256 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
257 ttwwakeup(tp);
258 return;
259 }
260
261 tp->t_state |= TS_BUSY;
262 while (tp->t_outq.c_cc != 0)
263 dcons_putc(dc, getc(&tp->t_outq));
264 tp->t_state &= ~TS_BUSY;
265
266 ttwwakeup(tp);
267 splx(s);
268 }
269
270 static void
271 dcons_timeout(void *v)
272 {
273 struct tty *tp;
274 struct dcons_softc *dc;
275 int i, c, polltime;
276
277 for (i = 0; i < DCONS_NPORT; i ++) {
278 dc = &sc[i];
279 tp = dc->dev->si_tty;
280 while ((c = dcons_checkc(dc)) != -1)
281 if (tp->t_state & TS_ISOPEN)
282 ttyld_rint(tp, c);
283 }
284 polltime = hz / poll_hz;
285 if (polltime < 1)
286 polltime = 1;
287 callout_reset(&dcons_callout, polltime, dcons_timeout, tp);
288 }
289
290 static void
291 dcons_cnprobe(struct consdev *cp)
292 {
293 #if __FreeBSD_version >= 501109
294 sprintf(cp->cn_name, "dcons");
295 #else
296 cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON);
297 #endif
298 #if DCONS_FORCE_CONSOLE
299 cp->cn_pri = CN_REMOTE;
300 #else
301 cp->cn_pri = CN_NORMAL;
302 #endif
303 }
304
305 static void
306 dcons_cninit(struct consdev *cp)
307 {
308 dcons_drv_init(0);
309 #if CONS_NODEV
310 cp->cn_arg
311 #else
312 cp->cn_dev->si_drv1
313 #endif
314 = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */
315 }
316
317 #if CONS_NODEV
318 static int
319 dcons_cngetc(struct consdev *cp)
320 {
321 return(dcons_getc((struct dcons_softc *)cp->cn_arg));
322 }
323 static int
324 dcons_cncheckc(struct consdev *cp)
325 {
326 return(dcons_checkc((struct dcons_softc *)cp->cn_arg));
327 }
328 static void
329 dcons_cnputc(struct consdev *cp, int c)
330 {
331 dcons_putc((struct dcons_softc *)cp->cn_arg, c);
332 }
333 #else
334 static int
335 dcons_cngetc(struct cdev *dev)
336 {
337 return(dcons_getc((struct dcons_softc *)dev->si_drv1));
338 }
339 static int
340 dcons_cncheckc(struct cdev *dev)
341 {
342 return(dcons_checkc((struct dcons_softc *)dev->si_drv1));
343 }
344 static void
345 dcons_cnputc(struct cdev *dev, int c)
346 {
347 dcons_putc((struct dcons_softc *)dev->si_drv1, c);
348 }
349 #endif
350
351 static int
352 dcons_getc(struct dcons_softc *dc)
353 {
354 int c;
355
356 while ((c = dcons_checkc(dc)) == -1);
357
358 return (c & 0xff);
359 }
360
361 static int
362 dcons_checkc(struct dcons_softc *dc)
363 {
364 unsigned char c;
365 u_int32_t ptr, pos, gen, next_gen;
366 struct dcons_ch *ch;
367
368 ch = &dc->i;
369
370 if (dg.dma_tag != NULL)
371 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD);
372 ptr = ntohl(*ch->ptr);
373 gen = ptr >> DCONS_GEN_SHIFT;
374 pos = ptr & DCONS_POS_MASK;
375 if (gen == ch->gen && pos == ch->pos)
376 return (-1);
377
378 next_gen = DCONS_NEXT_GEN(ch->gen);
379 /* XXX sanity check */
380 if ((gen != ch->gen && gen != next_gen)
381 || (gen == ch->gen && pos < ch->pos)) {
382 /* generation skipped !! */
383 /* XXX discard */
384 ch->gen = gen;
385 ch->pos = pos;
386 return (-1);
387 }
388
389 c = ch->buf[ch->pos];
390 ch->pos ++;
391 if (ch->pos >= ch->size) {
392 ch->gen = next_gen;
393 ch->pos = 0;
394 }
395
396 #if __FreeBSD_version >= 502122
397 #if KDB && ALT_BREAK_TO_DEBUGGER
398 if (kdb_alt_break(c, &dc->brk_state)) {
399 if ((dc->flags & DC_GDB) != 0) {
400 if (gdb_cur == &dcons_gdb_dbgport) {
401 kdb_dbbe_select("gdb");
402 breakpoint();
403 }
404 } else
405 breakpoint();
406 }
407 #endif
408 #else
409 #if DDB && ALT_BREAK_TO_DEBUGGER
410 switch (dc->brk_state) {
411 case STATE1:
412 if (c == KEY_TILDE)
413 dc->brk_state = STATE2;
414 else
415 dc->brk_state = STATE0;
416 break;
417 case STATE2:
418 dc->brk_state = STATE0;
419 if (c == KEY_CTRLB) {
420 #if DCONS_FORCE_GDB
421 if (dc->flags & DC_GDB)
422 boothowto |= RB_GDB;
423 #endif
424 breakpoint();
425 }
426 }
427 if (c == KEY_CR)
428 dc->brk_state = STATE1;
429 #endif
430 #endif
431 return (c);
432 }
433
434 static void
435 dcons_putc(struct dcons_softc *dc, int c)
436 {
437 struct dcons_ch *ch;
438
439 ch = &dc->o;
440
441 ch->buf[ch->pos] = c;
442 ch->pos ++;
443 if (ch->pos >= ch->size) {
444 ch->gen = DCONS_NEXT_GEN(ch->gen);
445 ch->pos = 0;
446 }
447 *ch->ptr = DCONS_MAKE_PTR(ch);
448 if (dg.dma_tag != NULL)
449 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE);
450 }
451
452 static int
453 dcons_init_port(int port, int offset, int size)
454 {
455 int osize;
456 struct dcons_softc *dc;
457
458 dc = &sc[port];
459
460 osize = size * 3 / 4;
461
462 dc->o.size = osize;
463 dc->i.size = size - osize;
464 dc->o.buf = (char *)dg.buf + offset;
465 dc->i.buf = dc->o.buf + osize;
466 dc->o.gen = dc->i.gen = 0;
467 dc->o.pos = dc->i.pos = 0;
468 dc->o.ptr = &dg.buf->optr[port];
469 dc->i.ptr = &dg.buf->iptr[port];
470 dc->brk_state = STATE0;
471 dg.buf->osize[port] = htonl(osize);
472 dg.buf->isize[port] = htonl(size - osize);
473 dg.buf->ooffset[port] = htonl(offset);
474 dg.buf->ioffset[port] = htonl(offset + osize);
475 dg.buf->optr[port] = DCONS_MAKE_PTR(&dc->o);
476 dg.buf->iptr[port] = DCONS_MAKE_PTR(&dc->i);
477
478 return(0);
479 }
480
481 static int
482 dcons_drv_init(int stage)
483 {
484 int size, size0, offset;
485
486 if (drv_init)
487 return(drv_init);
488
489 drv_init = -1;
490
491 bzero(&dg, sizeof(dg));
492 dcons_conf = &dg;
493 dg.cdev = &dcons_consdev;
494 dg.size = DCONS_BUF_SIZE;
495
496 #ifndef KLD_MODULE
497 if (stage == 0) /* XXX or cold */
498 /*
499 * DCONS_FORCE_CONSOLE == 1 and statically linked.
500 * called from cninit(). can't use contigmalloc yet .
501 */
502 dg.buf = (struct dcons_buf *) bssbuf;
503 else
504 #endif
505 /*
506 * DCONS_FORCE_CONSOLE == 0 or kernel module case.
507 * if the module is loaded after boot,
508 * bssbuf could be non-continuous.
509 */
510 dg.buf = (struct dcons_buf *) contigmalloc(dg.size,
511 M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
512
513 dcons_buf = dg.buf;
514 offset = DCONS_HEADER_SIZE;
515 size = (dg.size - offset);
516 size0 = size * 3 / 4;
517
518 dcons_init_port(0, offset, size0);
519 offset += size0;
520 dcons_init_port(1, offset, size - size0);
521 dg.buf->version = htonl(DCONS_VERSION);
522 dg.buf->magic = ntohl(DCONS_MAGIC);
523
524 #if __FreeBSD_version < 502122
525 #if DDB && DCONS_FORCE_GDB
526 #if CONS_NODEV
527 gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB];
528 #if __FreeBSD_version >= 501109
529 sprintf(gdbconsdev.cn_name, "dgdb");
530 #endif
531 gdb_arg = &gdbconsdev;
532 #else
533 gdbdev = makedev(CDEV_MAJOR, DCONS_GDB);
534 #endif
535 gdb_getc = dcons_cngetc;
536 gdb_putc = dcons_cnputc;
537 #endif
538 #endif
539 drv_init = 1;
540
541 return 0;
542 }
543
544
545 static int
546 dcons_attach_port(int port, char *name, int flags)
547 {
548 struct dcons_softc *dc;
549 struct tty *tp;
550
551 dc = &sc[port];
552 dc->flags = flags;
553 dc->dev = make_dev(&dcons_cdevsw, port,
554 UID_ROOT, GID_WHEEL, 0600, name);
555 tp = ttymalloc(NULL);
556
557 dc->dev->si_drv1 = (void *)dc;
558 dc->dev->si_tty = tp;
559
560 tp->t_oproc = dcons_tty_start;
561 tp->t_param = dcons_tty_param;
562 tp->t_stop = nottystop;
563 tp->t_dev = dc->dev;
564
565 return(0);
566 }
567
568 static int
569 dcons_attach(void)
570 {
571 int polltime;
572
573 dcons_attach_port(DCONS_CON, "dcons", 0);
574 dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB);
575 #if __FreeBSD_version < 500000
576 callout_init(&dcons_callout);
577 #else
578 callout_init(&dcons_callout, 0);
579 #endif
580 polltime = hz / poll_hz;
581 if (polltime < 1)
582 polltime = 1;
583 callout_reset(&dcons_callout, polltime, dcons_timeout, NULL);
584 return(0);
585 }
586
587 static int
588 dcons_detach(int port)
589 {
590 struct tty *tp;
591 struct dcons_softc *dc;
592
593 dc = &sc[port];
594
595 tp = dc->dev->si_tty;
596
597 if (tp->t_state & TS_ISOPEN) {
598 printf("dcons: still opened\n");
599 ttyld_close(tp, 0);
600 tty_close(tp);
601 }
602 /* XXX
603 * must wait until all device are closed.
604 */
605 tsleep((void *)dc, PWAIT, "dcodtc", hz/4);
606 destroy_dev(dc->dev);
607
608 return(0);
609 }
610
611
612 /* cnXXX works only for FreeBSD-5 */
613 static int
614 dcons_modevent(module_t mode, int type, void *data)
615 {
616 int err = 0, ret;
617
618 switch (type) {
619 case MOD_LOAD:
620 ret = dcons_drv_init(1);
621 dcons_attach();
622 #if __FreeBSD_version >= 500000
623 if (ret == 0) {
624 dcons_cnprobe(&dcons_consdev);
625 dcons_cninit(&dcons_consdev);
626 cnadd(&dcons_consdev);
627 }
628 #endif
629 break;
630 case MOD_UNLOAD:
631 printf("dcons: unload\n");
632 callout_stop(&dcons_callout);
633 #if __FreeBSD_version < 502122
634 #if DDB && DCONS_FORCE_GDB
635 #if CONS_NODEV
636 gdb_arg = NULL;
637 #else
638 gdbdev = NULL;
639 #endif
640 #endif
641 #endif
642 #if __FreeBSD_version >= 500000
643 cnremove(&dcons_consdev);
644 #endif
645 dcons_detach(DCONS_CON);
646 dcons_detach(DCONS_GDB);
647 dg.buf->magic = 0;
648
649 contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF);
650
651 break;
652 case MOD_SHUTDOWN:
653 break;
654 default:
655 err = EOPNOTSUPP;
656 break;
657 }
658 return(err);
659 }
660
661 #if __FreeBSD_version >= 502122
662 /* Debugger interface */
663
664 static int
665 dcons_dbg_probe(void)
666 {
667 return(DCONS_FORCE_GDB);
668 }
669
670 static void
671 dcons_dbg_init(void)
672 {
673 }
674
675 static void
676 dcons_dbg_term(void)
677 {
678 }
679
680 static void
681 dcons_dbg_putc(int c)
682 {
683 dcons_putc(&sc[DCONS_GDB], c);
684 }
685
686 static int
687 dcons_dbg_checkc(void)
688 {
689 return (dcons_checkc(&sc[DCONS_GDB]));
690 }
691
692 static int
693 dcons_dbg_getc(void)
694 {
695 return (dcons_getc(&sc[DCONS_GDB]));
696 }
697 #endif
698
699 DEV_MODULE(dcons, dcons_modevent, NULL);
700 MODULE_VERSION(dcons, DCONS_VERSION);
Cache object: 8b2d3f3ef5e535a5282fc05c81c70b2b
|