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