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