Add dcons(4), a pseudo console driver for FireWire and KVM interface.
[dragonfly.git] / sys / dev / misc / 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: src/sys/dev/dcons/dcons.c,v 1.16 2004/07/15 20:47:37 phk Exp $
36  * $DragonFly: src/sys/dev/misc/dcons/dcons.c,v 1.1 2004/09/23 05:09:49 simokawa Exp $
37  */
38
39 #include <sys/param.h>
40 #if __FreeBSD_version >= 502122
41 #include <sys/kdb.h>
42 #include <gdb/gdb.h>
43 #endif
44 #include <sys/kernel.h>
45 #include <sys/module.h>
46 #include <sys/systm.h>
47 #include <sys/types.h>
48 #include <sys/conf.h>
49 #include <sys/cons.h>
50 #include <sys/consio.h>
51 #include <sys/tty.h>
52 #include <sys/malloc.h>
53 #include <sys/proc.h>
54 #include <sys/ucred.h>
55
56 #include <machine/bus.h>
57
58 #ifdef __DragonFly__
59 #include "dcons.h"
60 #else
61 #include <dev/dcons/dcons.h>
62 #endif
63
64 #include <ddb/ddb.h>
65 #include <sys/reboot.h>
66
67 #include <sys/sysctl.h>
68
69 #include "opt_ddb.h"
70 #include "opt_comconsole.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     1       /* 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
263         return (0);
264 }
265
266 #if defined(__DragonFly__) || __FreeBSD_version < 500104
267 static int
268 dcons_ioctl(DEV dev, u_long cmd, caddr_t data, int flag, THREAD *td)
269 {
270         int     unit;
271         struct  tty *tp;
272         int     error;
273
274         unit = minor(dev);
275         if (unit != 0)
276                 return (ENXIO);
277
278         tp = dev->si_tty;
279         error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
280         if (error != ENOIOCTL)
281                 return (error);
282
283         error = ttioctl(tp, cmd, data, flag);
284         if (error != ENOIOCTL)
285                 return (error);
286
287         return (ENOTTY);
288 }
289 #endif
290
291 static int
292 dcons_tty_param(struct tty *tp, struct termios *t)
293 {
294         tp->t_ispeed = t->c_ispeed;
295         tp->t_ospeed = t->c_ospeed;
296         tp->t_cflag = t->c_cflag;
297         return 0;
298 }
299
300 static void
301 dcons_tty_start(struct tty *tp)
302 {
303         struct dcons_softc *dc;
304         int s;
305
306         dc = (struct dcons_softc *)tp->t_dev->si_drv1;
307         s = spltty();
308         if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
309                 ttwwakeup(tp);
310                 return;
311         }
312
313         tp->t_state |= TS_BUSY;
314         while (tp->t_outq.c_cc != 0)
315                 dcons_putc(dc, getc(&tp->t_outq));
316         tp->t_state &= ~TS_BUSY;
317
318         ttwwakeup(tp);
319         splx(s);
320 }
321
322 static void
323 dcons_timeout(void *v)
324 {
325         struct  tty *tp;
326         struct dcons_softc *dc;
327         int i, c, polltime;
328
329         for (i = 0; i < DCONS_NPORT; i ++) {
330                 dc = &sc[i];
331                 tp = dc->dev->si_tty;
332                 while ((c = dcons_checkc(dc)) != -1)
333                         if (tp->t_state & TS_ISOPEN)
334 #if __FreeBSD_version < 502113
335                                 (*linesw[tp->t_line].l_rint)(c, tp);
336 #else
337                                 ttyld_rint(tp, c);
338 #endif
339         }
340         polltime = hz / poll_hz;
341         if (polltime < 1)
342                 polltime = 1;
343         callout_reset(&dcons_callout, polltime, dcons_timeout, tp);
344 }
345
346 static void
347 dcons_cnprobe(struct consdev *cp)
348 {
349 #ifdef __DragonFly__
350         cp->cn_dev = make_dev(&dcons_cdevsw, DCONS_CON,
351             UID_ROOT, GID_WHEEL, 0600, "dcons");
352 #elif __FreeBSD_version >= 501109
353         sprintf(cp->cn_name, "dcons");
354 #else
355         cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON);
356 #endif
357 #if DCONS_FORCE_CONSOLE
358         cp->cn_pri = CN_REMOTE;
359 #else
360         cp->cn_pri = CN_NORMAL;
361 #endif
362 }
363
364 static void
365 dcons_cninit(struct consdev *cp)
366 {
367         dcons_drv_init(0);
368 #if CONS_NODEV
369         cp->cn_arg
370 #else
371         cp->cn_dev->si_drv1
372 #endif
373                 = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */
374 }
375
376 #if CONS_NODEV
377 static int
378 dcons_cngetc(struct consdev *cp)
379 {
380         return(dcons_getc((struct dcons_softc *)cp->cn_arg));
381 }
382 static int
383 dcons_cncheckc(struct consdev *cp)
384 {
385         return(dcons_checkc((struct dcons_softc *)cp->cn_arg)); 
386 }
387 static void
388 dcons_cnputc(struct consdev *cp, int c)
389 {
390         dcons_putc((struct dcons_softc *)cp->cn_arg, c);
391 }
392 #else
393 static int
394 dcons_cngetc(DEV dev)
395 {
396         return(dcons_getc((struct dcons_softc *)dev->si_drv1));
397 }
398 static int
399 dcons_cncheckc(DEV dev)
400 {
401         return(dcons_checkc((struct dcons_softc *)dev->si_drv1));
402 }
403 static void
404 dcons_cnputc(DEV dev, int c)
405 {
406         dcons_putc((struct dcons_softc *)dev->si_drv1, c);
407 }
408 #endif
409
410 static int
411 dcons_getc(struct dcons_softc *dc)
412 {
413         int c;
414
415         while ((c = dcons_checkc(dc)) == -1);
416
417         return (c & 0xff);
418 }
419
420 static int
421 dcons_checkc(struct dcons_softc *dc)
422 {
423         unsigned char c;
424         u_int32_t ptr, pos, gen, next_gen;
425         struct dcons_ch *ch;
426
427         ch = &dc->i;
428
429         if (dg.dma_tag != NULL)
430                 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD);
431         ptr = ntohl(*ch->ptr);
432         gen = ptr >> DCONS_GEN_SHIFT;
433         pos = ptr & DCONS_POS_MASK;
434         if (gen == ch->gen && pos == ch->pos)
435                 return (-1);
436
437         next_gen = DCONS_NEXT_GEN(ch->gen);
438         /* XXX sanity check */
439         if ((gen != ch->gen && gen != next_gen)
440                         || (gen == ch->gen && pos < ch->pos)) {
441                 /* generation skipped !! */
442                 /* XXX discard */
443                 ch->gen = gen;
444                 ch->pos = pos;
445                 return (-1);
446         }
447
448         c = ch->buf[ch->pos];
449         ch->pos ++;
450         if (ch->pos >= ch->size) {
451                 ch->gen = next_gen;
452                 ch->pos = 0;
453         }
454
455 #if __FreeBSD_version >= 502122
456 #if KDB && ALT_BREAK_TO_DEBUGGER
457         if (kdb_alt_break(c, &dc->brk_state)) {
458                 if ((dc->flags & DC_GDB) != 0) {
459                         if (gdb_cur == &dcons_gdb_dbgport) {
460                                 kdb_dbbe_select("gdb");
461                                 breakpoint();
462                         }
463                 } else
464                         breakpoint();
465         }
466 #endif
467 #else
468 #if DDB && ALT_BREAK_TO_DEBUGGER
469         switch (dc->brk_state) {
470         case STATE1:
471                 if (c == KEY_TILDE)
472                         dc->brk_state = STATE2;
473                 else
474                         dc->brk_state = STATE0;
475                 break;
476         case STATE2:
477                 dc->brk_state = STATE0;
478                 if (c == KEY_CTRLB) {
479 #if DCONS_FORCE_GDB
480                         if (dc->flags & DC_GDB)
481                                 boothowto |= RB_GDB;
482 #endif
483                         breakpoint();
484                 }
485         }
486         if (c == KEY_CR)
487                 dc->brk_state = STATE1;
488 #endif
489 #endif
490         return (c);
491 }
492
493 static void
494 dcons_putc(struct dcons_softc *dc, int c)
495 {
496         struct dcons_ch *ch;
497
498         ch = &dc->o;
499
500         ch->buf[ch->pos] = c;
501         ch->pos ++;
502         if (ch->pos >= ch->size) {
503                 ch->gen = DCONS_NEXT_GEN(ch->gen);
504                 ch->pos = 0;
505         }
506         *ch->ptr = DCONS_MAKE_PTR(ch);
507         if (dg.dma_tag != NULL)
508                 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE);
509 }
510
511 static int
512 dcons_init_port(int port, int offset, int size)
513 {
514         int osize;
515         struct dcons_softc *dc;
516
517         dc = &sc[port];
518
519         osize = size * 3 / 4;
520
521         dc->o.size = osize;
522         dc->i.size = size - osize;
523         dc->o.buf = (char *)dg.buf + offset;
524         dc->i.buf = dc->o.buf + osize;
525         dc->o.gen = dc->i.gen = 0;
526         dc->o.pos = dc->i.pos = 0;
527         dc->o.ptr = &dg.buf->optr[port];
528         dc->i.ptr = &dg.buf->iptr[port];
529         dc->brk_state = STATE0;
530         dg.buf->osize[port] = htonl(osize);
531         dg.buf->isize[port] = htonl(size - osize);
532         dg.buf->ooffset[port] = htonl(offset);
533         dg.buf->ioffset[port] = htonl(offset + osize);
534         dg.buf->optr[port] = DCONS_MAKE_PTR(&dc->o);
535         dg.buf->iptr[port] = DCONS_MAKE_PTR(&dc->i);
536
537         return(0);
538 }
539
540 static int
541 dcons_drv_init(int stage)
542 {
543         int size, size0, offset;
544
545         if (drv_init)
546                 return(drv_init);
547
548         drv_init = -1;
549
550         bzero(&dg, sizeof(dg));
551         dcons_conf = &dg;
552         dg.cdev = &dcons_consdev;
553         dg.size = DCONS_BUF_SIZE;
554
555 #ifndef KLD_MODULE
556         if (stage == 0) /* XXX or cold */
557                 /*
558                  * DCONS_FORCE_CONSOLE == 1 and statically linked.
559                  * called from cninit(). can't use contigmalloc yet .
560                  */
561                 dg.buf = (struct dcons_buf *) bssbuf;
562         else
563 #endif
564                 /*
565                  * DCONS_FORCE_CONSOLE == 0 or kernel module case.
566                  * if the module is loaded after boot,
567                  * bssbuf could be non-continuous.
568                  */ 
569                 dg.buf = (struct dcons_buf *) contigmalloc(dg.size,
570                         M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
571
572         dcons_buf = dg.buf;
573         offset = DCONS_HEADER_SIZE;
574         size = (dg.size - offset);
575         size0 = size * 3 / 4;
576
577         dcons_init_port(0, offset, size0);
578         offset += size0;
579         dcons_init_port(1, offset, size - size0);
580         dg.buf->version = htonl(DCONS_VERSION);
581         dg.buf->magic = ntohl(DCONS_MAGIC);
582
583 #if __FreeBSD_version < 502122
584 #if DDB && DCONS_FORCE_GDB
585 #if CONS_NODEV
586         gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB];
587 #if __FreeBSD_version >= 501109
588         sprintf(gdbconsdev.cn_name, "dgdb");
589 #endif
590         gdb_arg = &gdbconsdev;
591 #elif defined(__DragonFly__)
592         gdbdev = make_dev(&dcons_cdevsw, DCONS_GDB,
593             UID_ROOT, GID_WHEEL, 0600, "dgdb");
594 #else
595         gdbdev = makedev(CDEV_MAJOR, DCONS_GDB);
596 #endif
597         gdb_getc = dcons_cngetc;
598         gdb_putc = dcons_cnputc;
599 #endif
600 #endif
601         drv_init = 1;
602
603         return 0;
604 }
605
606
607 static int
608 dcons_attach_port(int port, char *name, int flags)
609 {
610         struct dcons_softc *dc;
611         struct tty *tp;
612
613         dc = &sc[port];
614         dc->flags = flags;
615         dc->dev = make_dev(&dcons_cdevsw, port,
616                         UID_ROOT, GID_WHEEL, 0600, name);
617         tp = ttymalloc(NULL);
618
619         dc->dev->si_drv1 = (void *)dc;
620         dc->dev->si_tty = tp;
621
622         tp->t_oproc = dcons_tty_start;
623         tp->t_param = dcons_tty_param;
624         tp->t_stop = nottystop;
625         tp->t_dev = dc->dev;
626
627         return(0);
628 }
629
630 static int
631 dcons_attach(void)
632 {
633         int polltime;
634
635 #ifdef __DragonFly__
636         cdevsw_add(&dcons_cdevsw, -1, 0);
637 #endif
638         dcons_attach_port(DCONS_CON, "dcons", 0);
639         dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB);
640 #if __FreeBSD_version < 500000
641         callout_init(&dcons_callout);
642 #else
643         callout_init(&dcons_callout, 0);
644 #endif
645         polltime = hz / poll_hz;
646         if (polltime < 1)
647                 polltime = 1;
648         callout_reset(&dcons_callout, polltime, dcons_timeout, NULL);
649         return(0);
650 }
651
652 static int
653 dcons_detach(int port)
654 {
655         struct  tty *tp;
656         struct dcons_softc *dc;
657
658         dc = &sc[port];
659
660         tp = dc->dev->si_tty;
661
662         if (tp->t_state & TS_ISOPEN) {
663                 printf("dcons: still opened\n");
664 #if __FreeBSD_version < 502113
665                 (*linesw[tp->t_line].l_close)(tp, 0);
666                 tp->t_gen++;
667                 ttyclose(tp);
668                 ttwakeup(tp);
669                 ttwwakeup(tp);
670 #else
671                 ttyld_close(tp, 0);
672                 tty_close(tp);
673 #endif
674         }
675         /* XXX
676          * must wait until all device are closed.
677          */
678 #ifdef __DragonFly__
679         tsleep((void *)dc, 0, "dcodtc", hz/4);
680 #else
681         tsleep((void *)dc, PWAIT, "dcodtc", hz/4);
682 #endif
683         destroy_dev(dc->dev);
684
685         return(0);
686 }
687
688
689 /* cnXXX works only for FreeBSD-5 */
690 static int
691 dcons_modevent(module_t mode, int type, void *data)
692 {
693         int err = 0, ret;
694
695         switch (type) {
696         case MOD_LOAD:
697                 ret = dcons_drv_init(1);
698                 dcons_attach();
699 #if __FreeBSD_version >= 500000
700                 if (ret == 0) {
701                         dcons_cnprobe(&dcons_consdev);
702                         dcons_cninit(&dcons_consdev);
703                         cnadd(&dcons_consdev);
704                 }
705 #endif
706                 break;
707         case MOD_UNLOAD:
708                 printf("dcons: unload\n");
709                 callout_stop(&dcons_callout);
710 #if __FreeBSD_version < 502122
711 #if DDB && DCONS_FORCE_GDB
712 #if CONS_NODEV
713                 gdb_arg = NULL;
714 #else
715                 gdbdev = NULL;
716 #endif
717 #endif
718 #endif
719 #if __FreeBSD_version >= 500000
720                 cnremove(&dcons_consdev);
721 #endif
722                 dcons_detach(DCONS_CON);
723                 dcons_detach(DCONS_GDB);
724                 dg.buf->magic = 0;
725
726                 contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF);
727
728                 break;
729         case MOD_SHUTDOWN:
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);