xref: /freebsd/sys/dev/dcons/dcons.c (revision bacb482d94518367b83ecea533ae07cc1b15da34)
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$
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 #ifdef __DragonFly__
58 #include "dcons.h"
59 #else
60 #include <dev/dcons/dcons.h>
61 #endif
62 
63 #include <ddb/ddb.h>
64 #include <sys/reboot.h>
65 
66 #include <sys/sysctl.h>
67 
68 #include "opt_ddb.h"
69 #include "opt_comconsole.h"
70 #include "opt_dcons.h"
71 
72 #ifndef DCONS_POLL_HZ
73 #define DCONS_POLL_HZ	100
74 #endif
75 
76 #ifndef DCONS_BUF_SIZE
77 #define DCONS_BUF_SIZE (16*1024)
78 #endif
79 
80 #ifndef DCONS_FORCE_CONSOLE
81 #define DCONS_FORCE_CONSOLE	0	/* Mostly for FreeBSD-4/DragonFly */
82 #endif
83 
84 #ifndef DCONS_FORCE_GDB
85 #define DCONS_FORCE_GDB	1
86 #endif
87 
88 #if __FreeBSD_version >= 500101
89 #define CONS_NODEV	1
90 #if __FreeBSD_version < 502122
91 static struct consdev gdbconsdev;
92 #endif
93 #endif
94 
95 static d_open_t		dcons_open;
96 static d_close_t	dcons_close;
97 #if defined(__DragonFly__) || __FreeBSD_version < 500104
98 static d_ioctl_t	dcons_ioctl;
99 #endif
100 
101 static struct cdevsw dcons_cdevsw = {
102 #ifdef __DragonFly__
103 #define CDEV_MAJOR      184
104 	"dcons", CDEV_MAJOR, D_TTY, NULL, 0,
105 	dcons_open, dcons_close, ttyread, ttywrite, dcons_ioctl,
106 	ttypoll, nommap, nostrategy, nodump, nopsize,
107 #elif __FreeBSD_version >= 500104
108 	.d_version =	D_VERSION,
109 	.d_open =	dcons_open,
110 	.d_close =	dcons_close,
111 	.d_name =	"dcons",
112 	.d_flags =	D_TTY | D_NEEDGIANT,
113 #else
114 #define CDEV_MAJOR      184
115 	/* open */	dcons_open,
116 	/* close */	dcons_close,
117 	/* read */	ttyread,
118 	/* write */	ttywrite,
119 	/* ioctl */	dcons_ioctl,
120 	/* poll */	ttypoll,
121 	/* mmap */	nommap,
122 	/* strategy */	nostrategy,
123 	/* name */	"dcons",
124 	/* major */	CDEV_MAJOR,
125 	/* dump */	nodump,
126 	/* psize */	nopsize,
127 	/* flags */	D_TTY,
128 #endif
129 };
130 
131 #ifndef KLD_MODULE
132 static char bssbuf[DCONS_BUF_SIZE];	/* buf in bss */
133 #endif
134 
135 /* global data */
136 static struct dcons_global dg;
137 struct dcons_global *dcons_conf;
138 static int poll_hz = DCONS_POLL_HZ;
139 
140 SYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console");
141 SYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0,
142 				"dcons polling rate");
143 
144 static int drv_init = 0;
145 static struct callout dcons_callout;
146 struct dcons_buf *dcons_buf;		/* for local dconschat */
147 
148 #ifdef __DragonFly__
149 #define DEV	dev_t
150 #define THREAD	d_thread_t
151 #elif __FreeBSD_version < 500000
152 #define DEV	dev_t
153 #define THREAD	struct proc
154 #else
155 #define DEV	struct cdev *
156 #define THREAD	struct thread
157 #endif
158 
159 /* per device data */
160 static struct dcons_softc {
161 	DEV dev;
162 	struct dcons_ch	o, i;
163 	int brk_state;
164 #define DC_GDB	1
165 	int flags;
166 } sc[DCONS_NPORT];
167 static void	dcons_tty_start(struct tty *);
168 static int	dcons_tty_param(struct tty *, struct termios *);
169 static void	dcons_timeout(void *);
170 static int	dcons_drv_init(int);
171 static int	dcons_getc(struct dcons_softc *);
172 static int	dcons_checkc(struct dcons_softc *);
173 static void	dcons_putc(struct dcons_softc *, int);
174 
175 static cn_probe_t	dcons_cnprobe;
176 static cn_init_t	dcons_cninit;
177 static cn_getc_t	dcons_cngetc;
178 static cn_checkc_t 	dcons_cncheckc;
179 static cn_putc_t	dcons_cnputc;
180 
181 CONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, NULL, dcons_cngetc,
182     dcons_cncheckc, dcons_cnputc, NULL);
183 
184 #if __FreeBSD_version >= 502122
185 static gdb_probe_f dcons_dbg_probe;
186 static gdb_init_f dcons_dbg_init;
187 static gdb_term_f dcons_dbg_term;
188 static gdb_getc_f dcons_dbg_getc;
189 static gdb_checkc_f dcons_dbg_checkc;
190 static gdb_putc_f dcons_dbg_putc;
191 
192 GDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term,
193     dcons_dbg_checkc, dcons_dbg_getc, dcons_dbg_putc);
194 
195 extern struct gdb_dbgport *gdb_cur;
196 #endif
197 
198 static int
199 dcons_open(DEV dev, int flag, int mode, THREAD *td)
200 {
201 	struct tty *tp;
202 	int unit, error, s;
203 
204 	unit = minor(dev);
205 	if (unit != 0)
206 		return (ENXIO);
207 
208 	tp = dev->si_tty = ttymalloc(dev->si_tty);
209 	tp->t_oproc = dcons_tty_start;
210 	tp->t_param = dcons_tty_param;
211 	tp->t_stop = nottystop;
212 	tp->t_dev = dev;
213 
214 	error = 0;
215 
216 	s = spltty();
217 	if ((tp->t_state & TS_ISOPEN) == 0) {
218 		tp->t_state |= TS_CARR_ON;
219 		ttychars(tp);
220 		tp->t_iflag = TTYDEF_IFLAG;
221 		tp->t_oflag = TTYDEF_OFLAG;
222 		tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
223 		tp->t_lflag = TTYDEF_LFLAG;
224 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
225 		ttsetwater(tp);
226 	} else if ((tp->t_state & TS_XCLUDE) && suser(td)) {
227 		splx(s);
228 		return (EBUSY);
229 	}
230 	splx(s);
231 
232 #if __FreeBSD_version < 502113
233 	error = (*linesw[tp->t_line].l_open)(dev, tp);
234 #else
235 	error = ttyld_open(tp, dev);
236 #endif
237 
238 	return (error);
239 }
240 
241 static int
242 dcons_close(DEV dev, int flag, int mode, THREAD *td)
243 {
244 	int	unit;
245 	struct	tty *tp;
246 
247 	unit = minor(dev);
248 	if (unit != 0)
249 		return (ENXIO);
250 
251 	tp = dev->si_tty;
252 	if (tp->t_state & TS_ISOPEN) {
253 #if __FreeBSD_version < 502113
254 		(*linesw[tp->t_line].l_close)(tp, flag);
255 		ttyclose(tp);
256 #else
257 		ttyld_close(tp, flag);
258 		tty_close(tp);
259 #endif
260 	}
261 
262 	return (0);
263 }
264 
265 #if defined(__DragonFly__) || __FreeBSD_version < 500104
266 static int
267 dcons_ioctl(DEV dev, u_long cmd, caddr_t data, int flag, THREAD *td)
268 {
269 	int	unit;
270 	struct	tty *tp;
271 	int	error;
272 
273 	unit = minor(dev);
274 	if (unit != 0)
275 		return (ENXIO);
276 
277 	tp = dev->si_tty;
278 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
279 	if (error != ENOIOCTL)
280 		return (error);
281 
282 	error = ttioctl(tp, cmd, data, flag);
283 	if (error != ENOIOCTL)
284 		return (error);
285 
286 	return (ENOTTY);
287 }
288 #endif
289 
290 static int
291 dcons_tty_param(struct tty *tp, struct termios *t)
292 {
293 	tp->t_ispeed = t->c_ispeed;
294 	tp->t_ospeed = t->c_ospeed;
295 	tp->t_cflag = t->c_cflag;
296 	return 0;
297 }
298 
299 static void
300 dcons_tty_start(struct tty *tp)
301 {
302 	struct dcons_softc *dc;
303 	int s;
304 
305 	dc = (struct dcons_softc *)tp->t_dev->si_drv1;
306 	s = spltty();
307 	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
308 		ttwwakeup(tp);
309 		return;
310 	}
311 
312 	tp->t_state |= TS_BUSY;
313 	while (tp->t_outq.c_cc != 0)
314 		dcons_putc(dc, getc(&tp->t_outq));
315 	tp->t_state &= ~TS_BUSY;
316 
317 	ttwwakeup(tp);
318 	splx(s);
319 }
320 
321 static void
322 dcons_timeout(void *v)
323 {
324 	struct	tty *tp;
325 	struct dcons_softc *dc;
326 	int i, c, polltime;
327 
328 	for (i = 0; i < DCONS_NPORT; i ++) {
329 		dc = &sc[i];
330 		tp = dc->dev->si_tty;
331 		while ((c = dcons_checkc(dc)) != -1)
332 			if (tp->t_state & TS_ISOPEN)
333 #if __FreeBSD_version < 502113
334 				(*linesw[tp->t_line].l_rint)(c, tp);
335 #else
336 				ttyld_rint(tp, c);
337 #endif
338 	}
339 	polltime = hz / poll_hz;
340 	if (polltime < 1)
341 		polltime = 1;
342 	callout_reset(&dcons_callout, polltime, dcons_timeout, tp);
343 }
344 
345 static void
346 dcons_cnprobe(struct consdev *cp)
347 {
348 #ifdef __DragonFly__
349 	cp->cn_dev = make_dev(&dcons_cdevsw, DCONS_CON,
350 	    UID_ROOT, GID_WHEEL, 0600, "dcons");
351 #elif __FreeBSD_version >= 501109
352 	sprintf(cp->cn_name, "dcons");
353 #else
354 	cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON);
355 #endif
356 #if DCONS_FORCE_CONSOLE
357 	cp->cn_pri = CN_REMOTE;
358 #else
359 	cp->cn_pri = CN_NORMAL;
360 #endif
361 }
362 
363 static void
364 dcons_cninit(struct consdev *cp)
365 {
366 	dcons_drv_init(0);
367 #if CONS_NODEV
368 	cp->cn_arg
369 #else
370 	cp->cn_dev->si_drv1
371 #endif
372 		= (void *)&sc[DCONS_CON]; /* share port0 with unit0 */
373 }
374 
375 #if CONS_NODEV
376 static int
377 dcons_cngetc(struct consdev *cp)
378 {
379 	return(dcons_getc((struct dcons_softc *)cp->cn_arg));
380 }
381 static int
382 dcons_cncheckc(struct consdev *cp)
383 {
384 	return(dcons_checkc((struct dcons_softc *)cp->cn_arg));
385 }
386 static void
387 dcons_cnputc(struct consdev *cp, int c)
388 {
389 	dcons_putc((struct dcons_softc *)cp->cn_arg, c);
390 }
391 #else
392 static int
393 dcons_cngetc(DEV dev)
394 {
395 	return(dcons_getc((struct dcons_softc *)dev->si_drv1));
396 }
397 static int
398 dcons_cncheckc(DEV dev)
399 {
400 	return(dcons_checkc((struct dcons_softc *)dev->si_drv1));
401 }
402 static void
403 dcons_cnputc(DEV dev, int c)
404 {
405 	dcons_putc((struct dcons_softc *)dev->si_drv1, c);
406 }
407 #endif
408 
409 static int
410 dcons_getc(struct dcons_softc *dc)
411 {
412 	int c;
413 
414 	while ((c = dcons_checkc(dc)) == -1);
415 
416 	return (c & 0xff);
417 }
418 
419 static int
420 dcons_checkc(struct dcons_softc *dc)
421 {
422 	unsigned char c;
423 	u_int32_t ptr, pos, gen, next_gen;
424 	struct dcons_ch *ch;
425 
426 	ch = &dc->i;
427 
428 	if (dg.dma_tag != NULL)
429 		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD);
430 	ptr = ntohl(*ch->ptr);
431 	gen = ptr >> DCONS_GEN_SHIFT;
432 	pos = ptr & DCONS_POS_MASK;
433 	if (gen == ch->gen && pos == ch->pos)
434 		return (-1);
435 
436 	next_gen = DCONS_NEXT_GEN(ch->gen);
437 	/* XXX sanity check */
438 	if ((gen != ch->gen && gen != next_gen)
439 			|| (gen == ch->gen && pos < ch->pos)) {
440 		/* generation skipped !! */
441 		/* XXX discard */
442 		ch->gen = gen;
443 		ch->pos = pos;
444 		return (-1);
445 	}
446 
447 	c = ch->buf[ch->pos];
448 	ch->pos ++;
449 	if (ch->pos >= ch->size) {
450 		ch->gen = next_gen;
451 		ch->pos = 0;
452 	}
453 
454 #if __FreeBSD_version >= 502122
455 #if KDB && ALT_BREAK_TO_DEBUGGER
456 	if (kdb_alt_break(c, &dc->brk_state)) {
457 		if ((dc->flags & DC_GDB) != 0) {
458 			if (gdb_cur == &dcons_gdb_dbgport) {
459 				kdb_dbbe_select("gdb");
460 				breakpoint();
461 			}
462 		} else
463 			breakpoint();
464 	}
465 #endif
466 #else
467 #if DDB && ALT_BREAK_TO_DEBUGGER
468 	switch (dc->brk_state) {
469 	case STATE1:
470 		if (c == KEY_TILDE)
471 			dc->brk_state = STATE2;
472 		else
473 			dc->brk_state = STATE0;
474 		break;
475 	case STATE2:
476 		dc->brk_state = STATE0;
477 		if (c == KEY_CTRLB) {
478 #if DCONS_FORCE_GDB
479 			if (dc->flags & DC_GDB)
480 				boothowto |= RB_GDB;
481 #endif
482 			breakpoint();
483 		}
484 	}
485 	if (c == KEY_CR)
486 		dc->brk_state = STATE1;
487 #endif
488 #endif
489 	return (c);
490 }
491 
492 static void
493 dcons_putc(struct dcons_softc *dc, int c)
494 {
495 	struct dcons_ch *ch;
496 
497 	ch = &dc->o;
498 
499 	ch->buf[ch->pos] = c;
500 	ch->pos ++;
501 	if (ch->pos >= ch->size) {
502 		ch->gen = DCONS_NEXT_GEN(ch->gen);
503 		ch->pos = 0;
504 	}
505 	*ch->ptr = DCONS_MAKE_PTR(ch);
506 	if (dg.dma_tag != NULL)
507 		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE);
508 }
509 
510 static int
511 dcons_init_port(int port, int offset, int size)
512 {
513 	int osize;
514 	struct dcons_softc *dc;
515 
516 	dc = &sc[port];
517 
518 	osize = size * 3 / 4;
519 
520 	dc->o.size = osize;
521 	dc->i.size = size - osize;
522 	dc->o.buf = (char *)dg.buf + offset;
523 	dc->i.buf = dc->o.buf + osize;
524 	dc->o.gen = dc->i.gen = 0;
525 	dc->o.pos = dc->i.pos = 0;
526 	dc->o.ptr = &dg.buf->optr[port];
527 	dc->i.ptr = &dg.buf->iptr[port];
528 	dc->brk_state = STATE0;
529 	dg.buf->osize[port] = htonl(osize);
530 	dg.buf->isize[port] = htonl(size - osize);
531 	dg.buf->ooffset[port] = htonl(offset);
532 	dg.buf->ioffset[port] = htonl(offset + osize);
533 	dg.buf->optr[port] = DCONS_MAKE_PTR(&dc->o);
534 	dg.buf->iptr[port] = DCONS_MAKE_PTR(&dc->i);
535 
536 	return(0);
537 }
538 
539 static int
540 dcons_drv_init(int stage)
541 {
542 	int size, size0, offset;
543 
544 	if (drv_init)
545 		return(drv_init);
546 
547 	drv_init = -1;
548 
549 	bzero(&dg, sizeof(dg));
550 	dcons_conf = &dg;
551 	dg.cdev = &dcons_consdev;
552 	dg.size = DCONS_BUF_SIZE;
553 
554 #ifndef KLD_MODULE
555 	if (stage == 0) /* XXX or cold */
556 		/*
557 		 * DCONS_FORCE_CONSOLE == 1 and statically linked.
558 		 * called from cninit(). can't use contigmalloc yet .
559 		 */
560 		dg.buf = (struct dcons_buf *) bssbuf;
561 	else
562 #endif
563 		/*
564 		 * DCONS_FORCE_CONSOLE == 0 or kernel module case.
565 		 * if the module is loaded after boot,
566 		 * bssbuf could be non-continuous.
567 		 */
568 		dg.buf = (struct dcons_buf *) contigmalloc(dg.size,
569 			M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
570 
571 	dcons_buf = dg.buf;
572 	offset = DCONS_HEADER_SIZE;
573 	size = (dg.size - offset);
574 	size0 = size * 3 / 4;
575 
576 	dcons_init_port(0, offset, size0);
577 	offset += size0;
578 	dcons_init_port(1, offset, size - size0);
579 	dg.buf->version = htonl(DCONS_VERSION);
580 	dg.buf->magic = ntohl(DCONS_MAGIC);
581 
582 #if __FreeBSD_version < 502122
583 #if DDB && DCONS_FORCE_GDB
584 #if CONS_NODEV
585 	gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB];
586 #if __FreeBSD_version >= 501109
587 	sprintf(gdbconsdev.cn_name, "dgdb");
588 #endif
589 	gdb_arg = &gdbconsdev;
590 #elif defined(__DragonFly__)
591 	gdbdev = make_dev(&dcons_cdevsw, DCONS_GDB,
592 	    UID_ROOT, GID_WHEEL, 0600, "dgdb");
593 #else
594 	gdbdev = makedev(CDEV_MAJOR, DCONS_GDB);
595 #endif
596 	gdb_getc = dcons_cngetc;
597 	gdb_putc = dcons_cnputc;
598 #endif
599 #endif
600 	drv_init = 1;
601 
602 	return 0;
603 }
604 
605 
606 static int
607 dcons_attach_port(int port, char *name, int flags)
608 {
609 	struct dcons_softc *dc;
610 	struct tty *tp;
611 
612 	dc = &sc[port];
613 	dc->flags = flags;
614 	dc->dev = make_dev(&dcons_cdevsw, port,
615 			UID_ROOT, GID_WHEEL, 0600, name);
616 	tp = ttymalloc(NULL);
617 
618 	dc->dev->si_drv1 = (void *)dc;
619 	dc->dev->si_tty = tp;
620 
621 	tp->t_oproc = dcons_tty_start;
622 	tp->t_param = dcons_tty_param;
623 	tp->t_stop = nottystop;
624 	tp->t_dev = dc->dev;
625 
626 	return(0);
627 }
628 
629 static int
630 dcons_attach(void)
631 {
632 	int polltime;
633 
634 #ifdef __DragonFly__
635 	cdevsw_add(&dcons_cdevsw, -1, 0);
636 #endif
637 	dcons_attach_port(DCONS_CON, "dcons", 0);
638 	dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB);
639 #if __FreeBSD_version < 500000
640 	callout_init(&dcons_callout);
641 #else
642 	callout_init(&dcons_callout, 0);
643 #endif
644 	polltime = hz / poll_hz;
645 	if (polltime < 1)
646 		polltime = 1;
647 	callout_reset(&dcons_callout, polltime, dcons_timeout, NULL);
648 	return(0);
649 }
650 
651 static int
652 dcons_detach(int port)
653 {
654 	struct	tty *tp;
655 	struct dcons_softc *dc;
656 
657 	dc = &sc[port];
658 
659 	tp = dc->dev->si_tty;
660 
661 	if (tp->t_state & TS_ISOPEN) {
662 		printf("dcons: still opened\n");
663 #if __FreeBSD_version < 502113
664 		(*linesw[tp->t_line].l_close)(tp, 0);
665 		tp->t_gen++;
666 		ttyclose(tp);
667 		ttwakeup(tp);
668 		ttwwakeup(tp);
669 #else
670 		ttyld_close(tp, 0);
671 		tty_close(tp);
672 #endif
673 	}
674 	/* XXX
675 	 * must wait until all device are closed.
676 	 */
677 #ifdef __DragonFly__
678 	tsleep((void *)dc, 0, "dcodtc", hz/4);
679 #else
680 	tsleep((void *)dc, PWAIT, "dcodtc", hz/4);
681 #endif
682 	destroy_dev(dc->dev);
683 
684 	return(0);
685 }
686 
687 
688 /* cnXXX works only for FreeBSD-5 */
689 static int
690 dcons_modevent(module_t mode, int type, void *data)
691 {
692 	int err = 0, ret;
693 
694 	switch (type) {
695 	case MOD_LOAD:
696 		ret = dcons_drv_init(1);
697 		dcons_attach();
698 #if __FreeBSD_version >= 500000
699 		if (ret == 0) {
700 			dcons_cnprobe(&dcons_consdev);
701 			dcons_cninit(&dcons_consdev);
702 			cnadd(&dcons_consdev);
703 		}
704 #endif
705 		break;
706 	case MOD_UNLOAD:
707 		printf("dcons: unload\n");
708 		callout_stop(&dcons_callout);
709 #if __FreeBSD_version < 502122
710 #if DDB && DCONS_FORCE_GDB
711 #if CONS_NODEV
712 		gdb_arg = NULL;
713 #else
714 		gdbdev = NULL;
715 #endif
716 #endif
717 #endif
718 #if __FreeBSD_version >= 500000
719 		cnremove(&dcons_consdev);
720 #endif
721 		dcons_detach(DCONS_CON);
722 		dcons_detach(DCONS_GDB);
723 		dg.buf->magic = 0;
724 
725 		contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF);
726 
727 		break;
728 	case MOD_SHUTDOWN:
729 		dg.buf->magic = 0;
730 		break;
731 	default:
732 		err = EOPNOTSUPP;
733 		break;
734 	}
735 	return(err);
736 }
737 
738 #if __FreeBSD_version >= 502122
739 /* Debugger interface */
740 
741 static int
742 dcons_dbg_probe(void)
743 {
744 	return(DCONS_FORCE_GDB);
745 }
746 
747 static void
748 dcons_dbg_init(void)
749 {
750 }
751 
752 static void
753 dcons_dbg_term(void)
754 {
755 }
756 
757 static void
758 dcons_dbg_putc(int c)
759 {
760 	dcons_putc(&sc[DCONS_GDB], c);
761 }
762 
763 static int
764 dcons_dbg_checkc(void)
765 {
766 	return (dcons_checkc(&sc[DCONS_GDB]));
767 }
768 
769 static int
770 dcons_dbg_getc(void)
771 {
772 	return (dcons_getc(&sc[DCONS_GDB]));
773 }
774 #endif
775 
776 DEV_MODULE(dcons, dcons_modevent, NULL);
777 MODULE_VERSION(dcons, DCONS_VERSION);
778