xref: /openbsd-src/sys/arch/sparc64/dev/vcons.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: vcons.c,v 1.5 2008/11/24 16:19:33 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2008 Mark Kettenis
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/conf.h>
20 #include <sys/device.h>
21 #include <sys/proc.h>
22 #include <sys/systm.h>
23 #include <sys/tty.h>
24 
25 #ifdef DDB
26 #include <ddb/db_var.h>
27 #endif
28 
29 #include <machine/autoconf.h>
30 #include <machine/conf.h>
31 #include <machine/hypervisor.h>
32 #include <machine/openfirm.h>
33 
34 #include <dev/cons.h>
35 #include <sparc64/dev/vbusvar.h>
36 
37 struct vcons_softc {
38 	struct device	sc_dv;
39 	void		*sc_ih;
40 	struct tty	*sc_tty;
41 	void		*sc_si;
42 };
43 
44 int	vcons_match(struct device *, void *, void *);
45 void	vcons_attach(struct device *, struct device *, void *);
46 
47 struct cfattach vcons_ca = {
48 	sizeof(struct vcons_softc), vcons_match, vcons_attach
49 };
50 
51 struct cfdriver vcons_cd = {
52 	NULL, "vcons", DV_DULL
53 };
54 
55 int	vcons_intr(void *);
56 
57 int	vcons_cnlookc(dev_t, int *);
58 int	vcons_cngetc(dev_t);
59 void	vcons_cnputc(dev_t, int);
60 
61 void	vconsstart(struct tty *);
62 int	vconsparam(struct tty *, struct termios *);
63 void	vcons_softintr(void *);
64 
65 int
66 vcons_match(struct device *parent, void *match, void *aux)
67 {
68 	struct vbus_attach_args *va = aux;
69 
70 	if (strcmp(va->va_name, "console") == 0)
71 		return (1);
72 
73 	return (0);
74 }
75 
76 void
77 vcons_attach(struct device *parent, struct device *self, void *aux)
78 {
79 	struct vcons_softc *sc = (struct vcons_softc *)self;
80 	struct vbus_attach_args *va = aux;
81 	uint64_t sysino;
82 	int maj;
83 
84 	sc->sc_si = softintr_establish(IPL_TTY, vcons_softintr, sc);
85 	if (sc->sc_si == NULL)
86 		panic(": can't establish soft interrupt");
87 
88 	if (vbus_intr_map(va->va_node, va->va_intr[0], &sysino))
89 		printf(": can't map interrupt\n");
90 	printf(": ivec 0x%lx\n", sysino);
91 
92 	sc->sc_ih = bus_intr_establish(va->va_bustag, sysino, IPL_TTY, 0, vcons_intr, sc, sc->sc_dv.dv_xname);
93 	if (sc->sc_ih == NULL) {
94 		printf("%s: can't establish interrupt\n", sc->sc_dv.dv_xname);
95 		return;
96 	}
97 
98 	cn_tab->cn_pollc = nullcnpollc;
99 	cn_tab->cn_getc = vcons_cngetc;
100 	cn_tab->cn_putc = vcons_cnputc;
101 
102 	/* Locate the major number. */
103 	for (maj = 0; maj < nchrdev; maj++)
104 		if (cdevsw[maj].d_open == vconsopen)
105 			break;
106 	cn_tab->cn_dev = makedev(maj, self->dv_unit);
107 }
108 
109 int
110 vcons_intr(void *arg)
111 {
112 	struct vcons_softc *sc = arg;
113 
114 	if (sc->sc_tty)
115 		softintr_schedule(sc->sc_si);
116 	return (1);
117 }
118 
119 int
120 vcons_cnlookc(dev_t dev, int *cp)
121 {
122 	int64_t ch;
123 
124 	if (hv_cons_getchar(&ch) == H_EOK) {
125 #ifdef DDB
126 		if (ch == -1 && db_console)
127 			Debugger();
128 #endif
129 		*cp = ch;
130 		return (1);
131 	}
132 
133 	return (0);
134 }
135 
136 int
137 vcons_cngetc(dev_t dev)
138 {
139 	int c;
140 
141 	while(!vcons_cnlookc(dev, &c))
142 		;
143 
144 	return (c);
145 }
146 
147 void
148 vcons_cnputc(dev_t dev, int c)
149 {
150 	while (hv_cons_putchar(c) == H_EWOULDBLOCK);
151 }
152 
153 int
154 vconsopen(dev_t dev, int flag, int mode, struct proc *p)
155 {
156 	struct vcons_softc *sc;
157 	struct tty *tp;
158 	int unit = minor(dev);
159 
160 	if (unit > vcons_cd.cd_ndevs)
161 		return (ENXIO);
162 	sc = vcons_cd.cd_devs[unit];
163 	if (sc == NULL)
164 		return (ENXIO);
165 
166 	if (sc->sc_tty)
167 		tp = sc->sc_tty;
168 	else
169 		tp = sc->sc_tty = ttymalloc();
170 
171 	tp->t_oproc = vconsstart;
172 	tp->t_param = vconsparam;
173 	tp->t_dev = dev;
174 	if ((tp->t_state & TS_ISOPEN) == 0) {
175 		ttychars(tp);
176 		tp->t_iflag = TTYDEF_IFLAG;
177 		tp->t_oflag = TTYDEF_OFLAG;
178 		tp->t_cflag = TTYDEF_CFLAG;
179 		tp->t_lflag = TTYDEF_LFLAG;
180 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
181 		ttsetwater(tp);
182 	} else if ((tp->t_state & TS_XCLUDE) && suser(p, 0))
183 		return (EBUSY);
184 	tp->t_state |= TS_CARR_ON;
185 
186 	return ((*linesw[tp->t_line].l_open)(dev, tp));
187 }
188 
189 int
190 vconsclose(dev_t dev, int flag, int mode, struct proc *p)
191 {
192 	struct vcons_softc *sc;
193 	struct tty *tp;
194 	int unit = minor(dev);
195 
196 	if (unit > vcons_cd.cd_ndevs)
197 		return (ENXIO);
198 	sc = vcons_cd.cd_devs[unit];
199 	if (sc == NULL)
200 		return (ENXIO);
201 
202 	tp = sc->sc_tty;
203 	(*linesw[tp->t_line].l_close)(tp, flag);
204 	ttyclose(tp);
205 	return (0);
206 }
207 
208 int
209 vconsread(dev_t dev, struct uio *uio, int flag)
210 {
211 	struct vcons_softc *sc;
212 	struct tty *tp;
213 	int unit = minor(dev);
214 
215 	if (unit > vcons_cd.cd_ndevs)
216 		return (ENXIO);
217 	sc = vcons_cd.cd_devs[unit];
218 	if (sc == NULL)
219 		return (ENXIO);
220 
221 	tp = sc->sc_tty;
222 	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
223 }
224 
225 int
226 vconswrite(dev_t dev, struct uio *uio, int flag)
227 {
228 	struct vcons_softc *sc;
229 	struct tty *tp;
230 	int unit = minor(dev);
231 
232 	if (unit > vcons_cd.cd_ndevs)
233 		return (ENXIO);
234 	sc = vcons_cd.cd_devs[unit];
235 	if (sc == NULL)
236 		return (ENXIO);
237 
238 	tp = sc->sc_tty;
239 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
240 }
241 
242 int
243 vconsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
244 {
245 	struct vcons_softc *sc;
246 	struct tty *tp;
247 	int unit = minor(dev);
248 	int error;
249 
250 	if (unit > vcons_cd.cd_ndevs)
251 		return (ENXIO);
252 	sc = vcons_cd.cd_devs[unit];
253 	if (sc == NULL)
254 		return (ENXIO);
255 
256 	tp = sc->sc_tty;
257 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
258 	if (error >= 0)
259 		return error;
260 	error = ttioctl(tp, cmd, data, flag, p);
261 	if (error >= 0)
262 		return (error);
263 
264 	return (ENOTTY);
265 }
266 
267 void
268 vconsstart(struct tty *tp)
269 {
270 	int s;
271 
272 	s = spltty();
273 	if (tp->t_state & (TS_TTSTOP | TS_BUSY)) {
274 		splx(s);
275 		return;
276 	}
277 	if (tp->t_outq.c_cc <= tp->t_lowat) {
278 		if (tp->t_state & TS_ASLEEP) {
279 			tp->t_state &= ~TS_ASLEEP;
280 			wakeup((caddr_t)&tp->t_outq);
281 		}
282 		selwakeup(&tp->t_wsel);
283 	}
284 	tp->t_state |= TS_BUSY;
285 	while (tp->t_outq.c_cc != 0)
286 		vcons_cnputc(tp->t_dev, getc(&tp->t_outq));
287 	tp->t_state &= ~TS_BUSY;
288 	splx(s);
289 }
290 
291 int
292 vconsstop(struct tty *tp, int flag)
293 {
294 	int s;
295 
296 	s = spltty();
297 	if (tp->t_state & TS_BUSY)
298 		if ((tp->t_state & TS_TTSTOP) == 0)
299 			tp->t_state |= TS_FLUSH;
300 	splx(s);
301 	return (0);
302 }
303 
304 struct tty *
305 vconstty(dev_t dev)
306 {
307 	struct vcons_softc *sc;
308 	int unit = minor(dev);
309 
310 	if (unit > vcons_cd.cd_ndevs)
311 		return (NULL);
312 	sc = vcons_cd.cd_devs[unit];
313 	if (sc == NULL)
314 		return (NULL);
315 
316 	return sc->sc_tty;
317 }
318 
319 int
320 vconsparam(struct tty *tp, struct termios *t)
321 {
322 	tp->t_ispeed = t->c_ispeed;
323 	tp->t_ospeed = t->c_ospeed;
324 	tp->t_cflag = t->c_cflag;
325 	return (0);
326 }
327 
328 void
329 vcons_softintr(void *arg)
330 {
331 	struct vcons_softc *sc = arg;
332 	struct tty *tp = sc->sc_tty;
333 	int c;
334 
335 	while (vcons_cnlookc(tp->t_dev, &c)) {
336 		if (tp->t_state & TS_ISOPEN)
337 			(*linesw[tp->t_line].l_rint)(c, tp);
338 	}
339 }
340