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