xref: /openbsd-src/sys/arch/sparc64/dev/vcons.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: vcons.c,v 1.14 2014/05/10 11:49:31 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 vcons_is_input, vcons_is_output;
83 	int node, maj;
84 
85 	sc->sc_si = softintr_establish(IPL_TTY, vcons_softintr, sc);
86 	if (sc->sc_si == NULL)
87 		panic(": can't establish soft interrupt");
88 
89 	if (vbus_intr_map(va->va_node, va->va_intr[0], &sysino))
90 		printf(": can't map interrupt\n");
91 	printf(": ivec 0x%llx", sysino);
92 
93 	sc->sc_ih = bus_intr_establish(va->va_bustag, sysino, IPL_TTY,
94 	    BUS_INTR_ESTABLISH_MPSAFE, vcons_intr, sc, sc->sc_dv.dv_xname);
95 	if (sc->sc_ih == NULL) {
96 		printf(", can't establish interrupt\n");
97 		return;
98 	}
99 
100 	node = OF_instance_to_package(OF_stdin());
101 	vcons_is_input = (va->va_node == node);
102 	node = OF_instance_to_package(OF_stdout());
103 	vcons_is_output = (va->va_node == node);
104 
105 	if (vcons_is_input || vcons_is_output) {
106 		if (vcons_is_input) {
107 			cn_tab->cn_pollc = nullcnpollc;
108 			cn_tab->cn_getc = vcons_cngetc;
109 
110 			/* Locate the major number. */
111 			for (maj = 0; maj < nchrdev; maj++)
112 				if (cdevsw[maj].d_open == vconsopen)
113 					break;
114 			cn_tab->cn_dev = makedev(maj, self->dv_unit);
115 		}
116 		if (vcons_is_output)
117 			cn_tab->cn_putc = vcons_cnputc;
118 
119 		printf(", console");
120 	}
121 
122 	printf("\n");
123 }
124 
125 int
126 vcons_intr(void *arg)
127 {
128 	struct vcons_softc *sc = arg;
129 
130 	if (sc->sc_tty)
131 		softintr_schedule(sc->sc_si);
132 	return (1);
133 }
134 
135 int
136 vcons_cnlookc(dev_t dev, int *cp)
137 {
138 	int64_t ch;
139 
140 	if (hv_cons_getchar(&ch) == H_EOK) {
141 #ifdef DDB
142 		if (ch == -1 && db_console)
143 			Debugger();
144 #endif
145 		*cp = ch;
146 		return (1);
147 	}
148 
149 	return (0);
150 }
151 
152 int
153 vcons_cngetc(dev_t dev)
154 {
155 	int c;
156 
157 	while(!vcons_cnlookc(dev, &c))
158 		;
159 
160 	return (c);
161 }
162 
163 void
164 vcons_cnputc(dev_t dev, int c)
165 {
166 	while (hv_cons_putchar(c) == H_EWOULDBLOCK);
167 }
168 
169 int
170 vconsopen(dev_t dev, int flag, int mode, struct proc *p)
171 {
172 	struct vcons_softc *sc;
173 	struct tty *tp;
174 	int unit = minor(dev);
175 
176 	if (unit >= vcons_cd.cd_ndevs)
177 		return (ENXIO);
178 	sc = vcons_cd.cd_devs[unit];
179 	if (sc == NULL)
180 		return (ENXIO);
181 
182 	if (sc->sc_tty)
183 		tp = sc->sc_tty;
184 	else
185 		tp = sc->sc_tty = ttymalloc(0);
186 
187 	tp->t_oproc = vconsstart;
188 	tp->t_param = vconsparam;
189 	tp->t_dev = dev;
190 	if ((tp->t_state & TS_ISOPEN) == 0) {
191 		ttychars(tp);
192 		tp->t_iflag = TTYDEF_IFLAG;
193 		tp->t_oflag = TTYDEF_OFLAG;
194 		tp->t_cflag = TTYDEF_CFLAG;
195 		tp->t_lflag = TTYDEF_LFLAG;
196 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
197 		ttsetwater(tp);
198 	} else if ((tp->t_state & TS_XCLUDE) && suser(p, 0))
199 		return (EBUSY);
200 	tp->t_state |= TS_CARR_ON;
201 
202 	return ((*linesw[tp->t_line].l_open)(dev, tp, p));
203 }
204 
205 int
206 vconsclose(dev_t dev, int flag, int mode, struct proc *p)
207 {
208 	struct vcons_softc *sc;
209 	struct tty *tp;
210 	int unit = minor(dev);
211 
212 	if (unit >= vcons_cd.cd_ndevs)
213 		return (ENXIO);
214 	sc = vcons_cd.cd_devs[unit];
215 	if (sc == NULL)
216 		return (ENXIO);
217 
218 	tp = sc->sc_tty;
219 	(*linesw[tp->t_line].l_close)(tp, flag, p);
220 	ttyclose(tp);
221 	return (0);
222 }
223 
224 int
225 vconsread(dev_t dev, struct uio *uio, int flag)
226 {
227 	struct vcons_softc *sc;
228 	struct tty *tp;
229 	int unit = minor(dev);
230 
231 	if (unit >= vcons_cd.cd_ndevs)
232 		return (ENXIO);
233 	sc = vcons_cd.cd_devs[unit];
234 	if (sc == NULL)
235 		return (ENXIO);
236 
237 	tp = sc->sc_tty;
238 	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
239 }
240 
241 int
242 vconswrite(dev_t dev, struct uio *uio, int flag)
243 {
244 	struct vcons_softc *sc;
245 	struct tty *tp;
246 	int unit = minor(dev);
247 
248 	if (unit >= vcons_cd.cd_ndevs)
249 		return (ENXIO);
250 	sc = vcons_cd.cd_devs[unit];
251 	if (sc == NULL)
252 		return (ENXIO);
253 
254 	tp = sc->sc_tty;
255 	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
256 }
257 
258 int
259 vconsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
260 {
261 	struct vcons_softc *sc;
262 	struct tty *tp;
263 	int unit = minor(dev);
264 	int error;
265 
266 	if (unit >= vcons_cd.cd_ndevs)
267 		return (ENXIO);
268 	sc = vcons_cd.cd_devs[unit];
269 	if (sc == NULL)
270 		return (ENXIO);
271 
272 	tp = sc->sc_tty;
273 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
274 	if (error >= 0)
275 		return error;
276 	error = ttioctl(tp, cmd, data, flag, p);
277 	if (error >= 0)
278 		return (error);
279 
280 	return (ENOTTY);
281 }
282 
283 void
284 vconsstart(struct tty *tp)
285 {
286 	int s;
287 
288 	s = spltty();
289 	if (tp->t_state & (TS_TTSTOP | TS_BUSY)) {
290 		splx(s);
291 		return;
292 	}
293 	ttwakeupwr(tp);
294 	tp->t_state |= TS_BUSY;
295 	while (tp->t_outq.c_cc != 0)
296 		vcons_cnputc(tp->t_dev, getc(&tp->t_outq));
297 	tp->t_state &= ~TS_BUSY;
298 	splx(s);
299 }
300 
301 int
302 vconsstop(struct tty *tp, int flag)
303 {
304 	int s;
305 
306 	s = spltty();
307 	if (tp->t_state & TS_BUSY)
308 		if ((tp->t_state & TS_TTSTOP) == 0)
309 			tp->t_state |= TS_FLUSH;
310 	splx(s);
311 	return (0);
312 }
313 
314 struct tty *
315 vconstty(dev_t dev)
316 {
317 	struct vcons_softc *sc;
318 	int unit = minor(dev);
319 
320 	if (unit >= vcons_cd.cd_ndevs)
321 		return (NULL);
322 	sc = vcons_cd.cd_devs[unit];
323 	if (sc == NULL)
324 		return (NULL);
325 
326 	return sc->sc_tty;
327 }
328 
329 int
330 vconsparam(struct tty *tp, struct termios *t)
331 {
332 	tp->t_ispeed = t->c_ispeed;
333 	tp->t_ospeed = t->c_ospeed;
334 	tp->t_cflag = t->c_cflag;
335 	return (0);
336 }
337 
338 void
339 vcons_softintr(void *arg)
340 {
341 	struct vcons_softc *sc = arg;
342 	struct tty *tp = sc->sc_tty;
343 	int c;
344 
345 	while (vcons_cnlookc(tp->t_dev, &c)) {
346 		if (tp->t_state & TS_ISOPEN)
347 			(*linesw[tp->t_line].l_rint)(c, tp);
348 	}
349 }
350