cfe_console.c revision 183370
1/*-
2 * Copyright (c) 2007 Bruce M. Simpson.
3 * 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 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/cfe/cfe_console.c 183370 2008-09-26 03:53:10Z imp $");
29
30#include "opt_comconsole.h"
31
32#include <sys/param.h>
33#include <sys/kdb.h>
34#include <sys/kernel.h>
35#include <sys/priv.h>
36#include <sys/systm.h>
37#include <sys/types.h>
38#include <sys/conf.h>
39#include <sys/cons.h>
40#include <sys/consio.h>
41#include <sys/tty.h>
42
43#include <dev/cfe/cfe_api.h>
44#include <dev/cfe/cfe_error.h>
45
46#include <ddb/ddb.h>
47
48#ifndef	CFECONS_POLL_HZ
49#define	CFECONS_POLL_HZ	4	/* 50-100 works best on Ultra2 */
50#endif
51#define CFEBURSTLEN	128	/* max number of bytes to write in one chunk */
52
53static d_open_t		cfe_dev_open;
54static d_close_t	cfe_dev_close;
55
56static struct cdevsw cfe_cdevsw = {
57	.d_version =	D_VERSION,
58	.d_open =	cfe_dev_open,
59	.d_close =	cfe_dev_close,
60	.d_name =	"cfe",
61	.d_flags =	D_TTY | D_NEEDGIANT,
62};
63
64static int			conhandle = -1;
65static struct tty		*cfe_tp = NULL;
66/* XXX does cfe have to poll? */
67static int			polltime;
68static struct callout_handle	cfe_timeouthandle
69    = CALLOUT_HANDLE_INITIALIZER(&cfe_timeouthandle);
70
71#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
72static int			alt_break_state;
73#endif
74
75static void	cfe_tty_start(struct tty *);
76static int	cfe_tty_param(struct tty *, struct termios *);
77static void	cfe_tty_stop(struct tty *, int);
78static void	cfe_timeout(void *);
79
80static cn_probe_t	cfe_cnprobe;
81static cn_init_t	cfe_cninit;
82static cn_term_t	cfe_cnterm;
83static cn_getc_t	cfe_cngetc;
84static cn_putc_t	cfe_cnputc;
85
86CONSOLE_DRIVER(cfe);
87
88static void
89cn_drvinit(void *unused)
90{
91	char output[32];
92	struct cdev *dev;
93
94	if (cfe_consdev.cn_pri != CN_DEAD &&
95	    cfe_consdev.cn_name[0] != '\0') {
96		dev = make_dev(&cfe_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s",
97		    output);
98		make_dev_alias(dev, "cfecons");
99	}
100}
101
102static int
103cfe_dev_open(struct cdev *dev, int flag, int mode, struct thread *td)
104{
105	struct	tty *tp;
106	int	unit;
107	int	error, setuptimeout;
108
109	error = 0;
110	setuptimeout = 0;
111	unit = minor(dev);
112
113	/*
114	 * XXX: BAD, should happen at attach time
115	 */
116	if (dev->si_tty == NULL) {
117		cfe_tp = ttyalloc();
118		dev->si_tty = cfe_tp;
119		cfe_tp->t_dev = dev;
120	}
121	tp = dev->si_tty;
122
123	tp->t_oproc = cfe_tty_start;
124	tp->t_param = cfe_tty_param;
125	tp->t_stop = cfe_tty_stop;
126	tp->t_dev = dev;
127
128	if ((tp->t_state & TS_ISOPEN) == 0) {
129		tp->t_state |= TS_CARR_ON;
130		ttyconsolemode(tp, 0);
131
132		setuptimeout = 1;
133	} else if ((tp->t_state & TS_XCLUDE) &&
134	    priv_check(td, PRIV_TTY_EXCLUSIVE)) {
135		return (EBUSY);
136	}
137
138	error = ttyld_open(tp, dev);
139
140	if (error == 0 && setuptimeout) {
141		polltime = hz / CFECONS_POLL_HZ;
142		if (polltime < 1) {
143			polltime = 1;
144		}
145
146		cfe_timeouthandle = timeout(cfe_timeout, tp, polltime);
147	}
148
149	return (error);
150}
151
152static int
153cfe_dev_close(struct cdev *dev, int flag, int mode, struct thread *td)
154{
155	int	unit;
156	struct	tty *tp;
157
158	unit = minor(dev);
159	tp = dev->si_tty;
160
161	if (unit != 0) {
162		return (ENXIO);
163	}
164
165	/* XXX Should be replaced with callout_stop(9) */
166	untimeout(cfe_timeout, tp, cfe_timeouthandle);
167	ttyld_close(tp, flag);
168	tty_close(tp);
169
170	return (0);
171}
172
173
174static int
175cfe_tty_param(struct tty *tp, struct termios *t)
176{
177
178	return (0);
179}
180
181static void
182cfe_tty_start(struct tty *tp)
183{
184	struct clist *cl;
185	int len;
186	u_char buf[CFEBURSTLEN];
187
188	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
189		return;
190
191	tp->t_state |= TS_BUSY;
192	cl = &tp->t_outq;
193	len = q_to_b(cl, buf, CFEBURSTLEN);
194	while (cfe_write(conhandle, buf, len) == 0)
195		;
196	tp->t_state &= ~TS_BUSY;
197
198	ttwwakeup(tp);
199}
200
201static void
202cfe_tty_stop(struct tty *tp, int flag)
203{
204
205	if (tp->t_state & TS_BUSY) {
206		if ((tp->t_state & TS_TTSTOP) == 0) {
207			tp->t_state |= TS_FLUSH;
208		}
209	}
210}
211
212static void
213cfe_timeout(void *v)
214{
215	struct	tty *tp;
216	int 	c;
217
218	tp = (struct tty *)v;
219
220	while ((c = cfe_cngetc(NULL)) != -1) {
221		if (tp->t_state & TS_ISOPEN) {
222			ttyld_rint(tp, c);
223		}
224	}
225
226	cfe_timeouthandle = timeout(cfe_timeout, tp, polltime);
227}
228
229static void
230cfe_cnprobe(struct consdev *cp)
231{
232
233	conhandle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE);
234	if (conhandle < 0) {
235		cp->cn_pri = CN_DEAD;
236		return;
237	}
238
239	/* XXX */
240	if (bootverbose) {
241		char *bootmsg = "Using CFE firmware console.\n";
242		int i;
243
244		for (i = 0; i < strlen(bootmsg); i++)
245			cfe_cnputc(cp, bootmsg[i]);
246	}
247
248	cp->cn_pri = CN_LOW;
249}
250
251static void
252cfe_cninit(struct consdev *cp)
253{
254
255	sprintf(cp->cn_name, "cfecons");
256	cp->cn_tp = cfe_tp;
257}
258
259static void
260cfe_cnterm(struct consdev *cp)
261{
262
263}
264
265static int
266cfe_cngetc(struct consdev *cp)
267{
268	int result;
269	unsigned char ch;
270
271	while ((result = cfe_read(conhandle, &ch, 1)) == 0)
272		;
273
274	if (result > 0) {
275#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
276		if (kdb_alt_break(ch, &alt_break_state))
277			kdb_enter(KDB_WHY_BREAK, "Break sequence on console");
278#endif
279		return (ch);
280	}
281
282	return (-1);
283}
284
285static void
286cfe_cnputc(struct consdev *cp, int c)
287{
288	char cbuf;
289
290	if (c == '\n')
291		cfe_cnputc(cp, '\r');
292
293	cbuf = c;
294	while (cfe_write(conhandle, &cbuf, 1) == 0)
295		;
296}
297
298SYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL)
299