dcons_os.c revision 139749
1250323Sdteske/*-
2250323Sdteske * Copyright (C) 2003,2004
3250323Sdteske * 	Hidetoshi Shimokawa. All rights reserved.
4252980Sdteske *
5250323Sdteske * Redistribution and use in source and binary forms, with or without
6250323Sdteske * modification, are permitted provided that the following conditions
7250323Sdteske * are met:
8250323Sdteske * 1. Redistributions of source code must retain the above copyright
9250323Sdteske *    notice, this list of conditions and the following disclaimer.
10250323Sdteske * 2. Redistributions in binary form must reproduce the above copyright
11250323Sdteske *    notice, this list of conditions and the following disclaimer in the
12250323Sdteske *    documentation and/or other materials provided with the distribution.
13250323Sdteske * 3. All advertising materials mentioning features or use of this software
14250323Sdteske *    must display the following acknowledgement:
15250323Sdteske *
16252987Sdteske *	This product includes software developed by Hidetoshi Shimokawa.
17250323Sdteske *
18250323Sdteske * 4. Neither the name of the author nor the names of its contributors
19250323Sdteske *    may be used to endorse or promote products derived from this software
20252987Sdteske *    without specific prior written permission.
21250323Sdteske *
22250323Sdteske * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23250323Sdteske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24250323Sdteske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25250323Sdteske * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26250323Sdteske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27250323Sdteske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28250323Sdteske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29250323Sdteske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30250323Sdteske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31250323Sdteske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32250323Sdteske * SUCH DAMAGE.
33250323Sdteske *
34250323Sdteske * $FreeBSD: head/sys/dev/dcons/dcons_os.c 139749 2005-01-06 01:43:34Z imp $
35250323Sdteske */
36268999Sdteske
37250323Sdteske#include <sys/param.h>
38250323Sdteske#if __FreeBSD_version >= 502122
39250323Sdteske#include <sys/kdb.h>
40250323Sdteske#include <gdb/gdb.h>
41250323Sdteske#endif
42250323Sdteske#include <sys/kernel.h>
43250323Sdteske#include <sys/module.h>
44250323Sdteske#include <sys/systm.h>
45250323Sdteske#include <sys/types.h>
46250323Sdteske#include <sys/conf.h>
47257795Sdteske#include <sys/cons.h>
48257795Sdteske#include <sys/consio.h>
49257795Sdteske#include <sys/tty.h>
50257795Sdteske#include <sys/malloc.h>
51257795Sdteske#include <sys/proc.h>
52257795Sdteske#include <sys/ucred.h>
53257795Sdteske
54257795Sdteske#include <machine/bus.h>
55257795Sdteske
56257795Sdteske#ifdef __DragonFly__
57250323Sdteske#include "dcons.h"
58250323Sdteske#include "dcons_os.h"
59257795Sdteske#else
60250323Sdteske#include <dev/dcons/dcons.h>
61257795Sdteske#include <dev/dcons/dcons_os.h>
62257795Sdteske#endif
63257795Sdteske
64250323Sdteske#include <ddb/ddb.h>
65250323Sdteske#include <sys/reboot.h>
66250323Sdteske
67250323Sdteske#include <sys/sysctl.h>
68250323Sdteske
69258265Sdteske#include <vm/vm.h>
70268999Sdteske#include <vm/vm_param.h>
71250323Sdteske#include <vm/pmap.h>
72250323Sdteske
73250323Sdteske#include "opt_ddb.h"
74250323Sdteske#include "opt_comconsole.h"
75250323Sdteske#include "opt_dcons.h"
76250323Sdteske
77268999Sdteske#ifndef DCONS_POLL_HZ
78268999Sdteske#define DCONS_POLL_HZ	100
79268999Sdteske#endif
80250323Sdteske
81264840Sdteske#ifndef DCONS_BUF_SIZE
82250323Sdteske#define DCONS_BUF_SIZE (16*1024)
83257795Sdteske#endif
84257795Sdteske
85257795Sdteske#ifndef DCONS_FORCE_CONSOLE
86257795Sdteske#define DCONS_FORCE_CONSOLE	0	/* Mostly for FreeBSD-4/DragonFly */
87257795Sdteske#endif
88257795Sdteske
89257795Sdteske#ifndef DCONS_FORCE_GDB
90257795Sdteske#define DCONS_FORCE_GDB	1
91257795Sdteske#endif
92258267Sdteske
93258267Sdteske#if __FreeBSD_version >= 500101
94258267Sdteske#define CONS_NODEV	1
95258267Sdteske#if __FreeBSD_version < 502122
96258267Sdteskestatic struct consdev gdbconsdev;
97258267Sdteske#endif
98258267Sdteske#endif
99258267Sdteske
100258267Sdteskestatic d_open_t		dcons_open;
101258267Sdteskestatic d_close_t	dcons_close;
102258267Sdteske#if defined(__DragonFly__) || __FreeBSD_version < 500104
103258267Sdteskestatic d_ioctl_t	dcons_ioctl;
104258267Sdteske#endif
105259427Sgjb
106259427Sgjbstatic struct cdevsw dcons_cdevsw = {
107259427Sgjb#ifdef __DragonFly__
108258267Sdteske#define CDEV_MAJOR      184
109258267Sdteske	"dcons", CDEV_MAJOR, D_TTY, NULL, 0,
110257795Sdteske	dcons_open, dcons_close, ttyread, ttywrite, dcons_ioctl,
111257795Sdteske	ttypoll, nommap, nostrategy, nodump, nopsize,
112268999Sdteske#elif __FreeBSD_version >= 500104
113268999Sdteske	.d_version =	D_VERSION,
114268999Sdteske	.d_open =	dcons_open,
115268999Sdteske	.d_close =	dcons_close,
116257795Sdteske	.d_name =	"dcons",
117264840Sdteske	.d_flags =	D_TTY | D_NEEDGIANT,
118250323Sdteske#else
119250323Sdteske#define CDEV_MAJOR      184
120257795Sdteske	/* open */	dcons_open,
121257795Sdteske	/* close */	dcons_close,
122257795Sdteske	/* read */	ttyread,
123257795Sdteske	/* write */	ttywrite,
124257795Sdteske	/* ioctl */	dcons_ioctl,
125257795Sdteske	/* poll */	ttypoll,
126257795Sdteske	/* mmap */	nommap,
127257795Sdteske	/* strategy */	nostrategy,
128257795Sdteske	/* name */	"dcons",
129257795Sdteske	/* major */	CDEV_MAJOR,
130257795Sdteske	/* dump */	nodump,
131257795Sdteske	/* psize */	nopsize,
132257795Sdteske	/* flags */	D_TTY,
133264840Sdteske#endif
134257795Sdteske};
135257795Sdteske
136257795Sdteske#ifndef KLD_MODULE
137257795Sdteskestatic char bssbuf[DCONS_BUF_SIZE];	/* buf in bss */
138257795Sdteske#endif
139257795Sdteske
140257795Sdteske/* global data */
141257795Sdteskestatic struct dcons_global dg;
142257795Sdteskestruct dcons_global *dcons_conf;
143257795Sdteskestatic int poll_hz = DCONS_POLL_HZ;
144257795Sdteske
145257795Sdteskestatic struct dcons_softc sc[DCONS_NPORT];
146257795Sdteske
147257795SdteskeSYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console");
148257795SdteskeSYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0,
149257795Sdteske				"dcons polling rate");
150257795Sdteske
151257795Sdteskestatic int drv_init = 0;
152257795Sdteskestatic struct callout dcons_callout;
153257795Sdteskestruct dcons_buf *dcons_buf;		/* for local dconschat */
154257795Sdteske
155257795Sdteske#ifdef __DragonFly__
156257795Sdteske#define DEV	dev_t
157257795Sdteske#define THREAD	d_thread_t
158257795Sdteske#elif __FreeBSD_version < 500000
159257795Sdteske#define DEV	dev_t
160257795Sdteske#define THREAD	struct proc
161257795Sdteske#else
162257795Sdteske#define DEV	struct cdev *
163257795Sdteske#define THREAD	struct thread
164257795Sdteske#endif
165257795Sdteske
166257795Sdteske
167257795Sdteskestatic void	dcons_tty_start(struct tty *);
168257795Sdteskestatic int	dcons_tty_param(struct tty *, struct termios *);
169257795Sdteskestatic void	dcons_timeout(void *);
170257795Sdteskestatic int	dcons_drv_init(int);
171257795Sdteske
172257795Sdteskestatic cn_probe_t	dcons_cnprobe;
173257795Sdteskestatic cn_init_t	dcons_cninit;
174257795Sdteskestatic cn_getc_t	dcons_cngetc;
175257795Sdteskestatic cn_checkc_t 	dcons_cncheckc;
176257795Sdteskestatic cn_putc_t	dcons_cnputc;
177257795Sdteske
178257795SdteskeCONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, NULL, dcons_cngetc,
179257795Sdteske    dcons_cncheckc, dcons_cnputc, NULL);
180257795Sdteske
181257795Sdteske#if __FreeBSD_version >= 502122
182257795Sdteskestatic gdb_probe_f dcons_dbg_probe;
183268999Sdteskestatic gdb_init_f dcons_dbg_init;
184250323Sdteskestatic gdb_term_f dcons_dbg_term;
185257795Sdteskestatic gdb_getc_f dcons_dbg_getc;
186257795Sdteskestatic gdb_checkc_f dcons_dbg_checkc;
187257795Sdteskestatic gdb_putc_f dcons_dbg_putc;
188257795Sdteske
189257795SdteskeGDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term,
190259054Sdteske    dcons_dbg_checkc, dcons_dbg_getc, dcons_dbg_putc);
191259054Sdteske
192259054Sdteskeextern struct gdb_dbgport *gdb_cur;
193259054Sdteske#endif
194257795Sdteske
195257795Sdteske#if (KDB || DDB) && ALT_BREAK_TO_DEBUGGER
196257795Sdteskestatic int
197257795Sdteskedcons_check_break(struct dcons_softc *dc, int c)
198257795Sdteske{
199257795Sdteske	if (c < 0)
200267680Sdteske		return (c);
201259054Sdteske
202257795Sdteske#if __FreeBSD_version >= 502122
203259054Sdteske	if (kdb_alt_break(c, &dc->brk_state)) {
204259054Sdteske		if ((dc->flags & DC_GDB) != 0) {
205257795Sdteske			if (gdb_cur == &dcons_gdb_dbgport) {
206257795Sdteske				kdb_dbbe_select("gdb");
207250323Sdteske				breakpoint();
208250323Sdteske			}
209257795Sdteske		} else
210250323Sdteske			breakpoint();
211250323Sdteske	}
212250323Sdteske#else
213250323Sdteske	switch (dc->brk_state) {
214250323Sdteske	case STATE1:
215250323Sdteske		if (c == KEY_TILDE)
216250323Sdteske			dc->brk_state = STATE2;
217250323Sdteske		else
218250323Sdteske			dc->brk_state = STATE0;
219250323Sdteske		break;
220250323Sdteske	case STATE2:
221250323Sdteske		dc->brk_state = STATE0;
222250323Sdteske		if (c == KEY_CTRLB) {
223250323Sdteske#if DCONS_FORCE_GDB
224250323Sdteske			if (dc->flags & DC_GDB)
225250323Sdteske				boothowto |= RB_GDB;
226250323Sdteske#endif
227250323Sdteske			breakpoint();
228250323Sdteske		}
229250323Sdteske	}
230250323Sdteske	if (c == KEY_CR)
231250323Sdteske		dc->brk_state = STATE1;
232250323Sdteske#endif
233250323Sdteske	return (c);
234250323Sdteske}
235250323Sdteske#else
236250323Sdteske#define	dcons_check_break(dc, c)	(c)
237250323Sdteske#endif
238250323Sdteske
239250323Sdteskestatic int
240250323Sdteskedcons_os_checkc(struct dcons_softc *dc)
241250538Sdteske{
242250323Sdteske	int c;
243250323Sdteske
244250323Sdteske	if (dg.dma_tag != NULL)
245250323Sdteske		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD);
246273067Sdteske
247273067Sdteske	c = dcons_check_break(dc, dcons_checkc(dc));
248250323Sdteske
249273067Sdteske	if (dg.dma_tag != NULL)
250250323Sdteske		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREREAD);
251250323Sdteske
252250323Sdteske	return (c);
253250323Sdteske}
254250323Sdteske
255250323Sdteskestatic int
256250323Sdteskedcons_os_getc(struct dcons_softc *dc)
257250323Sdteske{
258250323Sdteske	int c;
259250323Sdteske
260250323Sdteske	while ((c = dcons_os_checkc(dc)) == -1);
261250323Sdteske
262250323Sdteske	return (c & 0xff);
263250323Sdteske}
264250323Sdteske
265250323Sdteskestatic void
266250323Sdteskedcons_os_putc(struct dcons_softc *dc, int c)
267250323Sdteske{
268250323Sdteske	if (dg.dma_tag != NULL)
269250323Sdteske		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTWRITE);
270250323Sdteske
271250323Sdteske	dcons_putc(dc, c);
272250538Sdteske
273250323Sdteske	if (dg.dma_tag != NULL)
274250323Sdteske		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE);
275250323Sdteske}
276250323Sdteskestatic int
277250323Sdteskedcons_open(DEV dev, int flag, int mode, THREAD *td)
278250538Sdteske{
279250410Sdteske	struct tty *tp;
280250323Sdteske	int unit, error, s;
281250323Sdteske
282250323Sdteske	unit = minor(dev);
283250410Sdteske	if (unit != 0)
284250323Sdteske		return (ENXIO);
285250323Sdteske
286250323Sdteske	tp = dev->si_tty = ttymalloc(dev->si_tty);
287250323Sdteske	tp->t_oproc = dcons_tty_start;
288250323Sdteske	tp->t_param = dcons_tty_param;
289250323Sdteske	tp->t_stop = nottystop;
290250323Sdteske	tp->t_dev = dev;
291250323Sdteske
292250323Sdteske	error = 0;
293250410Sdteske
294250323Sdteske	s = spltty();
295250323Sdteske	if ((tp->t_state & TS_ISOPEN) == 0) {
296250323Sdteske		tp->t_state |= TS_CARR_ON;
297250323Sdteske		ttyconsolemode(tp, 0);
298250323Sdteske		ttsetwater(tp);
299250323Sdteske	} else if ((tp->t_state & TS_XCLUDE) && suser(td)) {
300250323Sdteske		splx(s);
301250323Sdteske		return (EBUSY);
302250323Sdteske	}
303250323Sdteske	splx(s);
304250323Sdteske
305250323Sdteske#if __FreeBSD_version < 502113
306250323Sdteske	error = (*linesw[tp->t_line].l_open)(dev, tp);
307250323Sdteske#else
308250410Sdteske	error = ttyld_open(tp, dev);
309250323Sdteske#endif
310250323Sdteske
311250323Sdteske	return (error);
312250323Sdteske}
313250323Sdteske
314250323Sdteskestatic int
315250323Sdteskedcons_close(DEV dev, int flag, int mode, THREAD *td)
316250323Sdteske{
317250323Sdteske	int	unit;
318250323Sdteske	struct	tty *tp;
319250323Sdteske
320250323Sdteske	unit = minor(dev);
321250323Sdteske	if (unit != 0)
322250323Sdteske		return (ENXIO);
323250323Sdteske
324250323Sdteske	tp = dev->si_tty;
325250323Sdteske	if (tp->t_state & TS_ISOPEN) {
326250323Sdteske#if __FreeBSD_version < 502113
327250323Sdteske		(*linesw[tp->t_line].l_close)(tp, flag);
328250323Sdteske		ttyclose(tp);
329250323Sdteske#else
330250323Sdteske		ttyld_close(tp, flag);
331250323Sdteske		tty_close(tp);
332250323Sdteske#endif
333250323Sdteske	}
334250323Sdteske
335250323Sdteske	return (0);
336250323Sdteske}
337252738Sdteske
338252738Sdteske#if defined(__DragonFly__) || __FreeBSD_version < 500104
339250323Sdteskestatic int
340250323Sdteskedcons_ioctl(DEV dev, u_long cmd, caddr_t data, int flag, THREAD *td)
341250323Sdteske{
342250323Sdteske	int	unit;
343250323Sdteske	struct	tty *tp;
344250323Sdteske	int	error;
345250323Sdteske
346250323Sdteske	unit = minor(dev);
347250323Sdteske	if (unit != 0)
348252738Sdteske		return (ENXIO);
349250323Sdteske
350250323Sdteske	tp = dev->si_tty;
351250323Sdteske	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
352250323Sdteske	if (error != ENOIOCTL)
353250323Sdteske		return (error);
354250323Sdteske
355250538Sdteske	error = ttioctl(tp, cmd, data, flag);
356250538Sdteske	if (error != ENOIOCTL)
357250323Sdteske		return (error);
358250323Sdteske
359250323Sdteske	return (ENOTTY);
360250538Sdteske}
361250323Sdteske#endif
362250323Sdteske
363250323Sdteskestatic int
364250323Sdteskedcons_tty_param(struct tty *tp, struct termios *t)
365250323Sdteske{
366250323Sdteske	tp->t_ispeed = t->c_ispeed;
367250323Sdteske	tp->t_ospeed = t->c_ospeed;
368250323Sdteske	tp->t_cflag = t->c_cflag;
369250323Sdteske	return 0;
370250323Sdteske}
371250323Sdteske
372252745Sdteskestatic void
373252745Sdteskedcons_tty_start(struct tty *tp)
374252753Sdteske{
375252745Sdteske	struct dcons_softc *dc;
376252745Sdteske	int s;
377252745Sdteske
378252745Sdteske	dc = (struct dcons_softc *)tp->t_dev->si_drv1;
379252745Sdteske	s = spltty();
380252745Sdteske	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
381252745Sdteske		ttwwakeup(tp);
382252745Sdteske		return;
383252745Sdteske	}
384252745Sdteske
385252745Sdteske	tp->t_state |= TS_BUSY;
386252745Sdteske	while (tp->t_outq.c_cc != 0)
387252745Sdteske		dcons_os_putc(dc, getc(&tp->t_outq));
388252745Sdteske	tp->t_state &= ~TS_BUSY;
389252745Sdteske
390252745Sdteske	ttwwakeup(tp);
391252745Sdteske	splx(s);
392252745Sdteske}
393252745Sdteske
394252745Sdteskestatic void
395252745Sdteskedcons_timeout(void *v)
396252745Sdteske{
397252745Sdteske	struct	tty *tp;
398252745Sdteske	struct dcons_softc *dc;
399252745Sdteske	int i, c, polltime;
400252745Sdteske
401252745Sdteske	for (i = 0; i < DCONS_NPORT; i ++) {
402252745Sdteske		dc = &sc[i];
403252745Sdteske		tp = ((DEV)dc->dev)->si_tty;
404252745Sdteske		while ((c = dcons_os_checkc(dc)) != -1)
405252745Sdteske			if (tp->t_state & TS_ISOPEN)
406252745Sdteske#if __FreeBSD_version < 502113
407252745Sdteske				(*linesw[tp->t_line].l_rint)(c, tp);
408252745Sdteske#else
409252745Sdteske				ttyld_rint(tp, c);
410250323Sdteske#endif
411250323Sdteske	}
412250323Sdteske	polltime = hz / poll_hz;
413250323Sdteske	if (polltime < 1)
414250323Sdteske		polltime = 1;
415	callout_reset(&dcons_callout, polltime, dcons_timeout, tp);
416}
417
418static void
419dcons_cnprobe(struct consdev *cp)
420{
421#ifdef __DragonFly__
422	cp->cn_dev = make_dev(&dcons_cdevsw, DCONS_CON,
423	    UID_ROOT, GID_WHEEL, 0600, "dcons");
424#elif __FreeBSD_version >= 501109
425	sprintf(cp->cn_name, "dcons");
426#else
427	cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON);
428#endif
429#if DCONS_FORCE_CONSOLE
430	cp->cn_pri = CN_REMOTE;
431#else
432	cp->cn_pri = CN_NORMAL;
433#endif
434}
435
436static void
437dcons_cninit(struct consdev *cp)
438{
439	dcons_drv_init(0);
440#if CONS_NODEV
441	cp->cn_arg
442#else
443	cp->cn_dev->si_drv1
444#endif
445		= (void *)&sc[DCONS_CON]; /* share port0 with unit0 */
446}
447
448#if CONS_NODEV
449static int
450dcons_cngetc(struct consdev *cp)
451{
452	struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
453	return (dcons_os_getc(dc));
454}
455static int
456dcons_cncheckc(struct consdev *cp)
457{
458	struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
459	return (dcons_os_checkc(dc));
460}
461static void
462dcons_cnputc(struct consdev *cp, int c)
463{
464	struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
465	dcons_os_putc(dc, c);
466}
467#else
468static int
469dcons_cngetc(DEV dev)
470{
471	struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1;
472	return (dcons_os_getc(dc));
473}
474static int
475dcons_cncheckc(DEV dev)
476{
477	struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1;
478	return (dcons_os_checkc(dc));
479}
480static void
481dcons_cnputc(DEV dev, int c)
482{
483	struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1;
484	dcons_os_putc(dc, c);
485}
486#endif
487
488static int
489dcons_drv_init(int stage)
490{
491#if defined(__i386__) || defined(__amd64__)
492	quad_t addr, size;
493#endif
494
495	if (drv_init)
496		return(drv_init);
497
498	drv_init = -1;
499
500	bzero(&dg, sizeof(dg));
501	dcons_conf = &dg;
502	dg.cdev = &dcons_consdev;
503	dg.buf = NULL;
504	dg.size = DCONS_BUF_SIZE;
505
506#if defined(__i386__) || defined(__amd64__)
507	if (getenv_quad("dcons.addr", &addr) > 0 &&
508	    getenv_quad("dcons.size", &size) > 0) {
509#ifdef __i386__
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			*vtopte(KERNBASE + pa) |= PG_RW;
516		invltlb();
517#endif
518		/* XXX P to V */
519		dg.buf = (struct dcons_buf *)(vm_offset_t)(KERNBASE + addr);
520		dg.size = size;
521		if (dcons_load_buffer(dg.buf, dg.size, sc) < 0)
522			dg.buf = NULL;
523	}
524#endif
525	if (dg.buf != NULL)
526		goto ok;
527
528#ifndef KLD_MODULE
529	if (stage == 0) { /* XXX or cold */
530		/*
531		 * DCONS_FORCE_CONSOLE == 1 and statically linked.
532		 * called from cninit(). can't use contigmalloc yet .
533		 */
534		dg.buf = (struct dcons_buf *) bssbuf;
535		dcons_init(dg.buf, dg.size, sc);
536	} else
537#endif
538	{
539		/*
540		 * DCONS_FORCE_CONSOLE == 0 or kernel module case.
541		 * if the module is loaded after boot,
542		 * bssbuf could be non-continuous.
543		 */
544		dg.buf = (struct dcons_buf *) contigmalloc(dg.size,
545			M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
546		dcons_init(dg.buf, dg.size, sc);
547	}
548
549ok:
550	dcons_buf = dg.buf;
551
552#if __FreeBSD_version < 502122
553#if DDB && DCONS_FORCE_GDB
554#if CONS_NODEV
555	gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB];
556#if __FreeBSD_version >= 501109
557	sprintf(gdbconsdev.cn_name, "dgdb");
558#endif
559	gdb_arg = &gdbconsdev;
560#elif defined(__DragonFly__)
561	gdbdev = make_dev(&dcons_cdevsw, DCONS_GDB,
562	    UID_ROOT, GID_WHEEL, 0600, "dgdb");
563#else
564	gdbdev = makedev(CDEV_MAJOR, DCONS_GDB);
565#endif
566	gdb_getc = dcons_cngetc;
567	gdb_putc = dcons_cnputc;
568#endif
569#endif
570	drv_init = 1;
571
572	return 0;
573}
574
575
576static int
577dcons_attach_port(int port, char *name, int flags)
578{
579	struct dcons_softc *dc;
580	struct tty *tp;
581	DEV dev;
582
583	dc = &sc[port];
584	dc->flags = flags;
585	dev = make_dev(&dcons_cdevsw, port,
586			UID_ROOT, GID_WHEEL, 0600, name);
587	dc->dev = (void *)dev;
588	tp = ttymalloc(NULL);
589
590	dev->si_drv1 = (void *)dc;
591	dev->si_tty = tp;
592
593	tp->t_oproc = dcons_tty_start;
594	tp->t_param = dcons_tty_param;
595	tp->t_stop = nottystop;
596	tp->t_dev = dc->dev;
597
598	return(0);
599}
600
601static int
602dcons_attach(void)
603{
604	int polltime;
605
606#ifdef __DragonFly__
607	cdevsw_add(&dcons_cdevsw, -1, 0);
608#endif
609	dcons_attach_port(DCONS_CON, "dcons", 0);
610	dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB);
611#if __FreeBSD_version < 500000
612	callout_init(&dcons_callout);
613#else
614	callout_init(&dcons_callout, 0);
615#endif
616	polltime = hz / poll_hz;
617	if (polltime < 1)
618		polltime = 1;
619	callout_reset(&dcons_callout, polltime, dcons_timeout, NULL);
620	return(0);
621}
622
623static int
624dcons_detach(int port)
625{
626	struct	tty *tp;
627	struct dcons_softc *dc;
628
629	dc = &sc[port];
630
631	tp = ((DEV)dc->dev)->si_tty;
632
633	if (tp->t_state & TS_ISOPEN) {
634		printf("dcons: still opened\n");
635#if __FreeBSD_version < 502113
636		(*linesw[tp->t_line].l_close)(tp, 0);
637		tp->t_gen++;
638		ttyclose(tp);
639		ttwakeup(tp);
640		ttwwakeup(tp);
641#else
642		ttyld_close(tp, 0);
643		tty_close(tp);
644#endif
645	}
646	/* XXX
647	 * must wait until all device are closed.
648	 */
649#ifdef __DragonFly__
650	tsleep((void *)dc, 0, "dcodtc", hz/4);
651#else
652	tsleep((void *)dc, PWAIT, "dcodtc", hz/4);
653#endif
654	destroy_dev(dc->dev);
655
656	return(0);
657}
658
659
660/* cnXXX works only for FreeBSD-5 */
661static int
662dcons_modevent(module_t mode, int type, void *data)
663{
664	int err = 0, ret;
665
666	switch (type) {
667	case MOD_LOAD:
668		ret = dcons_drv_init(1);
669		dcons_attach();
670#if __FreeBSD_version >= 500000
671		if (ret == 0) {
672			dcons_cnprobe(&dcons_consdev);
673			dcons_cninit(&dcons_consdev);
674			cnadd(&dcons_consdev);
675		}
676#endif
677		break;
678	case MOD_UNLOAD:
679		printf("dcons: unload\n");
680		callout_stop(&dcons_callout);
681#if __FreeBSD_version < 502122
682#if DDB && DCONS_FORCE_GDB
683#if CONS_NODEV
684		gdb_arg = NULL;
685#else
686		gdbdev = NULL;
687#endif
688#endif
689#endif
690#if __FreeBSD_version >= 500000
691		cnremove(&dcons_consdev);
692#endif
693		dcons_detach(DCONS_CON);
694		dcons_detach(DCONS_GDB);
695		dg.buf->magic = 0;
696
697		contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF);
698
699		break;
700	case MOD_SHUTDOWN:
701		dg.buf->magic = 0;
702		break;
703	default:
704		err = EOPNOTSUPP;
705		break;
706	}
707	return(err);
708}
709
710#if __FreeBSD_version >= 502122
711/* Debugger interface */
712
713static int
714dcons_dbg_probe(void)
715{
716	return(DCONS_FORCE_GDB);
717}
718
719static void
720dcons_dbg_init(void)
721{
722}
723
724static void
725dcons_dbg_term(void)
726{
727}
728
729static void
730dcons_dbg_putc(int c)
731{
732	struct dcons_softc *dc = &sc[DCONS_GDB];
733	dcons_os_putc(dc, c);
734}
735
736static int
737dcons_dbg_checkc(void)
738{
739	struct dcons_softc *dc = &sc[DCONS_GDB];
740	return (dcons_os_checkc(dc));
741}
742
743static int
744dcons_dbg_getc(void)
745{
746	struct dcons_softc *dc = &sc[DCONS_GDB];
747	return (dcons_os_getc(dc));
748}
749#endif
750
751DEV_MODULE(dcons, dcons_modevent, NULL);
752MODULE_VERSION(dcons, DCONS_VERSION);
753