xref: /openbsd-src/sys/dev/wscons/wsdisplay_compat_usl.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: wsdisplay_compat_usl.c,v 1.31 2016/04/24 17:30:31 matthieu Exp $ */
2 /* $NetBSD: wsdisplay_compat_usl.c,v 1.12 2000/03/23 07:01:47 thorpej Exp $ */
3 
4 /*
5  * Copyright (c) 1998
6  *	Matthias Drochner.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/timeout.h>
33 #include <sys/kernel.h>
34 #include <sys/proc.h>
35 #include <sys/signalvar.h>
36 #include <sys/malloc.h>
37 #include <sys/errno.h>
38 
39 #include <dev/wscons/wsconsio.h>
40 #include <dev/wscons/wsdisplayvar.h>
41 #include <dev/wscons/wscons_callbacks.h>
42 #include <dev/wscons/wsdisplay_usl_io.h>
43 
44 #ifdef WSDISPLAY_DEBUG
45 #define DPRINTF(x)	 if (wsdisplaydebug) printf x
46 int	wsdisplaydebug = 0;
47 #else
48 #define DPRINTF(x)
49 #endif
50 
51 struct usl_syncdata {
52 	struct wsscreen *s_scr;
53 	struct process *s_process;
54 	pid_t s_pid;
55 	int s_flags;
56 #define SF_DETACHPENDING 1
57 #define SF_ATTACHPENDING 2
58 	int s_acqsig, s_relsig;
59 	int s_frsig; /* unused */
60 	void (*s_callback)(void *, int, int);
61 	void *s_cbarg;
62 	struct timeout s_attach_ch;
63 	struct timeout s_detach_ch;
64 };
65 
66 int usl_sync_init(struct wsscreen *, struct usl_syncdata **,
67 		       struct process *, int, int, int);
68 void usl_sync_done(struct usl_syncdata *);
69 int usl_sync_check(struct usl_syncdata *);
70 struct usl_syncdata *usl_sync_get(struct wsscreen *);
71 
72 int usl_detachproc(void *, int, void (*)(void *, int, int), void *);
73 int usl_detachack(struct usl_syncdata *, int);
74 void usl_detachtimeout(void *);
75 int usl_attachproc(void *, int, void (*)(void *, int, int), void *);
76 int usl_attachack(struct usl_syncdata *, int);
77 void usl_attachtimeout(void *);
78 
79 static const struct wscons_syncops usl_syncops = {
80 	usl_detachproc,
81 	usl_attachproc,
82 #define _usl_sync_check ((int (*)(void *))usl_sync_check)
83 	_usl_sync_check,
84 #define _usl_sync_destroy ((void (*)(void *))usl_sync_done)
85 	_usl_sync_destroy
86 };
87 
88 #ifndef WSCOMPAT_USL_SYNCTIMEOUT
89 #define WSCOMPAT_USL_SYNCTIMEOUT 5 /* seconds */
90 #endif
91 static int wscompat_usl_synctimeout = WSCOMPAT_USL_SYNCTIMEOUT;
92 
93 int
94 usl_sync_init(struct wsscreen *scr, struct usl_syncdata **sdp,
95     struct process *pr, int acqsig, int relsig, int frsig)
96 {
97 	struct usl_syncdata *sd;
98 	int res;
99 
100 	if (acqsig <= 0 || acqsig >= NSIG || relsig <= 0 || relsig >= NSIG ||
101 	    frsig <= 0 || frsig >= NSIG)
102 		return (EINVAL);
103 	sd = malloc(sizeof(*sd), M_DEVBUF, M_NOWAIT);
104 	if (!sd)
105 		return (ENOMEM);
106 	sd->s_scr = scr;
107 	sd->s_process = pr;
108 	sd->s_pid = pr->ps_pid;
109 	sd->s_flags = 0;
110 	sd->s_acqsig = acqsig;
111 	sd->s_relsig = relsig;
112 	sd->s_frsig = frsig;
113 	timeout_set(&sd->s_attach_ch, usl_attachtimeout, sd);
114 	timeout_set(&sd->s_detach_ch, usl_detachtimeout, sd);
115 	res = wsscreen_attach_sync(scr, &usl_syncops, sd);
116 	if (res) {
117 		free(sd, M_DEVBUF, sizeof(*sd));
118 		return (res);
119 	}
120 	*sdp = sd;
121 	return (0);
122 }
123 
124 void
125 usl_sync_done(struct usl_syncdata *sd)
126 {
127 	if (sd->s_flags & SF_DETACHPENDING) {
128 		timeout_del(&sd->s_detach_ch);
129 		(*sd->s_callback)(sd->s_cbarg, 0, 0);
130 	}
131 	if (sd->s_flags & SF_ATTACHPENDING) {
132 		timeout_del(&sd->s_attach_ch);
133 		(*sd->s_callback)(sd->s_cbarg, ENXIO, 0);
134 	}
135 	wsscreen_detach_sync(sd->s_scr);
136 	free(sd, M_DEVBUF, sizeof(*sd));
137 }
138 
139 int
140 usl_sync_check(struct usl_syncdata *sd)
141 {
142 	if (sd->s_process == prfind(sd->s_pid))
143 		return (1);
144 	DPRINTF(("usl_sync_check: process %d died\n", sd->s_pid));
145 	usl_sync_done(sd);
146 	return (0);
147 }
148 
149 struct usl_syncdata *
150 usl_sync_get(struct wsscreen *scr)
151 {
152 	struct usl_syncdata *sd;
153 
154 	if (wsscreen_lookup_sync(scr, &usl_syncops, (void **)&sd))
155 		return (0);
156 	return (sd);
157 }
158 
159 int
160 usl_detachproc(void *cookie, int waitok, void (*callback)(void *, int, int),
161     void *cbarg)
162 {
163 	struct usl_syncdata *sd = cookie;
164 
165 	if (!usl_sync_check(sd))
166 		return (0);
167 
168 	/* we really need a callback */
169 	if (!callback)
170 		return (EINVAL);
171 
172 	/*
173 	 * Normally, this is called from the controlling process.
174 	 * It is supposed to reply with a VT_RELDISP ioctl(), so
175 	 * it is not useful to tsleep() here.
176 	 */
177 	sd->s_callback = callback;
178 	sd->s_cbarg = cbarg;
179 	sd->s_flags |= SF_DETACHPENDING;
180 	prsignal(sd->s_process, sd->s_relsig);
181 	timeout_add_sec(&sd->s_detach_ch, wscompat_usl_synctimeout);
182 
183 	return (EAGAIN);
184 }
185 
186 int
187 usl_detachack(struct usl_syncdata *sd, int ack)
188 {
189 	if (!(sd->s_flags & SF_DETACHPENDING)) {
190 		DPRINTF(("usl_detachack: not detaching\n"));
191 		return (EINVAL);
192 	}
193 
194 	timeout_del(&sd->s_detach_ch);
195 	sd->s_flags &= ~SF_DETACHPENDING;
196 
197 	if (sd->s_callback)
198 		(*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1);
199 
200 	return (0);
201 }
202 
203 void
204 usl_detachtimeout(void *arg)
205 {
206 	struct usl_syncdata *sd = arg;
207 
208 	DPRINTF(("usl_detachtimeout\n"));
209 
210 	if (!(sd->s_flags & SF_DETACHPENDING)) {
211 		DPRINTF(("usl_detachtimeout: not detaching\n"));
212 		return;
213 	}
214 
215 	sd->s_flags &= ~SF_DETACHPENDING;
216 
217 	if (sd->s_callback)
218 		(*sd->s_callback)(sd->s_cbarg, EIO, 0);
219 
220 	(void) usl_sync_check(sd);
221 }
222 
223 int
224 usl_attachproc(void *cookie, int waitok, void (*callback)(void *, int, int),
225     void *cbarg)
226 {
227 	struct usl_syncdata *sd = cookie;
228 
229 	if (!usl_sync_check(sd))
230 		return (0);
231 
232 	/* we really need a callback */
233 	if (!callback)
234 		return (EINVAL);
235 
236 	sd->s_callback = callback;
237 	sd->s_cbarg = cbarg;
238 	sd->s_flags |= SF_ATTACHPENDING;
239 	prsignal(sd->s_process, sd->s_acqsig);
240 	timeout_add_sec(&sd->s_attach_ch, wscompat_usl_synctimeout);
241 
242 	return (EAGAIN);
243 }
244 
245 int
246 usl_attachack(struct usl_syncdata *sd, int ack)
247 {
248 	if (!(sd->s_flags & SF_ATTACHPENDING)) {
249 		DPRINTF(("usl_attachack: not attaching\n"));
250 		return (EINVAL);
251 	}
252 
253 	timeout_del(&sd->s_attach_ch);
254 	sd->s_flags &= ~SF_ATTACHPENDING;
255 
256 	if (sd->s_callback)
257 		(*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1);
258 
259 	return (0);
260 }
261 
262 void
263 usl_attachtimeout(void *arg)
264 {
265 	struct usl_syncdata *sd = arg;
266 
267 	DPRINTF(("usl_attachtimeout\n"));
268 
269 	if (!(sd->s_flags & SF_ATTACHPENDING)) {
270 		DPRINTF(("usl_attachtimeout: not attaching\n"));
271 		return;
272 	}
273 
274 	sd->s_flags &= ~SF_ATTACHPENDING;
275 
276 	if (sd->s_callback)
277 		(*sd->s_callback)(sd->s_cbarg, EIO, 0);
278 
279 	(void) usl_sync_check(sd);
280 }
281 
282 int
283 wsdisplay_usl_ioctl1(struct wsdisplay_softc *sc, u_long cmd, caddr_t data,
284     int flag, struct proc *p)
285 {
286 	int idx, maxidx;
287 
288 	switch (cmd) {
289 	    case VT_OPENQRY:
290 		maxidx = wsdisplay_maxscreenidx(sc);
291 		for (idx = 0; idx <= maxidx; idx++) {
292 			if (wsdisplay_screenstate(sc, idx) == 0) {
293 				*(int *)data = idx + 1;
294 				return (0);
295 			}
296 		}
297 		return (ENXIO);
298 	    case VT_GETACTIVE:
299 		idx = wsdisplay_getactivescreen(sc);
300 		*(int *)data = idx + 1;
301 		return (0);
302 	    case VT_ACTIVATE:
303 		idx = *(int *)data - 1;
304 		if (idx < 0)
305 			return (EINVAL);
306 		return (wsdisplay_switch((struct device *)sc, idx, 1));
307 	    case VT_WAITACTIVE:
308 		idx = *(int *)data - 1;
309 		if (idx < 0)
310 			return (EINVAL);
311 		return (wsscreen_switchwait(sc, idx));
312 	    case VT_GETSTATE:
313 #define ss ((struct vt_stat *)data)
314 		idx = wsdisplay_getactivescreen(sc);
315 		ss->v_active = idx + 1;
316 		ss->v_state = 0;
317 		maxidx = wsdisplay_maxscreenidx(sc);
318 		for (idx = 0; idx <= maxidx; idx++)
319 			if (wsdisplay_screenstate(sc, idx) == EBUSY)
320 				ss->v_state |= (1 << (idx + 1));
321 #undef ss
322 		return (0);
323 
324 	    default:
325 		return (-1);
326 	}
327 
328 	return (0);
329 }
330 
331 int
332 wsdisplay_usl_ioctl2(struct wsdisplay_softc *sc, struct wsscreen *scr,
333     u_long cmd, caddr_t data, int flag, struct proc *p)
334 {
335 	int intarg, res;
336 	u_long req;
337 	void *arg;
338 	struct usl_syncdata *sd;
339 	struct wskbd_bell_data bd;
340 
341 	switch (cmd) {
342 	    case VT_SETMODE:
343 #define newmode ((struct vt_mode *)data)
344 		if (newmode->mode == VT_PROCESS) {
345 			res = usl_sync_init(scr, &sd, p->p_p, newmode->acqsig,
346 					    newmode->relsig, newmode->frsig);
347 			if (res)
348 				return (res);
349 		} else {
350 			sd = usl_sync_get(scr);
351 			if (sd)
352 				usl_sync_done(sd);
353 		}
354 #undef newmode
355 		return (0);
356 	    case VT_GETMODE:
357 #define cmode ((struct vt_mode *)data)
358 		sd = usl_sync_get(scr);
359 		if (sd) {
360 			cmode->mode = VT_PROCESS;
361 			cmode->relsig = sd->s_relsig;
362 			cmode->acqsig = sd->s_acqsig;
363 			cmode->frsig = sd->s_frsig;
364 		} else
365 			cmode->mode = VT_AUTO;
366 #undef cmode
367 		return (0);
368 	    case VT_RELDISP:
369 #define d (*(int *)data)
370 		sd = usl_sync_get(scr);
371 		if (!sd)
372 			return (EINVAL);
373 		switch (d) {
374 		    case VT_FALSE:
375 		    case VT_TRUE:
376 			return (usl_detachack(sd, (d == VT_TRUE)));
377 		    case VT_ACKACQ:
378 			return (usl_attachack(sd, 1));
379 		    default:
380 			return (EINVAL);
381 		}
382 #undef d
383 		return (0);
384 
385 	    case KDENABIO:
386 	    case KDDISABIO:
387 		/*
388 		 * This is a lie, but non-x86 platforms are not supposed to
389 		 * issue these ioctls anyway.
390 		 */
391 		return (0);
392 
393 	    case KDSETRAD:
394 		/* XXX ignore for now */
395 		return (0);
396 
397 	    default:
398 		return (-1);
399 
400 	    /*
401 	     * the following are converted to wsdisplay ioctls
402 	     */
403 	    case KDSETMODE:
404 		req = WSDISPLAYIO_SMODE;
405 #define d (*(int *)data)
406 		switch (d) {
407 		    case KD_GRAPHICS:
408 			intarg = WSDISPLAYIO_MODE_MAPPED;
409 			break;
410 		    case KD_TEXT:
411 			intarg = WSDISPLAYIO_MODE_EMUL;
412 			break;
413 		    default:
414 			return (EINVAL);
415 		}
416 #undef d
417 		arg = &intarg;
418 		break;
419 	    case KDMKTONE:
420 		req = WSKBDIO_COMPLEXBELL;
421 #define d (*(int *)data)
422 		if (d) {
423 #define PCVT_SYSBEEPF	1193182
424 			if (d >> 16) {
425 				bd.which = WSKBD_BELL_DOPERIOD;
426 			bd.period = d >> 16; /* ms */
427 			}
428 			else
429 				bd.which = 0;
430 			if (d & 0xffff) {
431 				bd.which |= WSKBD_BELL_DOPITCH;
432 				bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */
433 			}
434 		} else
435 			bd.which = 0; /* default */
436 #undef d
437 		arg = &bd;
438 		break;
439 	    case KDSETLED:
440 		req = WSKBDIO_SETLEDS;
441 		intarg = 0;
442 #define d (*(int *)data)
443 		if (d & LED_CAP)
444 			intarg |= WSKBD_LED_CAPS;
445 		if (d & LED_NUM)
446 			intarg |= WSKBD_LED_NUM;
447 		if (d & LED_SCR)
448 			intarg |= WSKBD_LED_SCROLL;
449 #undef d
450 		arg = &intarg;
451 		break;
452 	    case KDGETLED:
453 		req = WSKBDIO_GETLEDS;
454 		arg = &intarg;
455 		break;
456 #ifdef WSDISPLAY_COMPAT_RAWKBD
457 	    case KDSKBMODE:
458 		req = WSKBDIO_SETMODE;
459 		switch (*(int *)data) {
460 		    case K_RAW:
461 			intarg = WSKBD_RAW;
462 			break;
463 		    case K_XLATE:
464 			intarg = WSKBD_TRANSLATED;
465 			break;
466 		    default:
467 			return (EINVAL);
468 		}
469 		arg = &intarg;
470 		break;
471 	    case KDGKBMODE:
472 		req = WSKBDIO_GETMODE;
473 		arg = &intarg;
474 		break;
475 #endif
476 	}
477 
478 	res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, p);
479 	if (res)
480 		return (res);
481 
482 	switch (cmd) {
483 	    case KDGETLED:
484 #define d (*(int *)data)
485 		d = 0;
486 		if (intarg & WSKBD_LED_CAPS)
487 			d |= LED_CAP;
488 		if (intarg & WSKBD_LED_NUM)
489 			d |= LED_NUM;
490 		if (intarg & WSKBD_LED_SCROLL)
491 			d |= LED_SCR;
492 #undef d
493 		break;
494 #ifdef WSDISPLAY_COMPAT_RAWKBD
495 	    case KDGKBMODE:
496 		*(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE);
497 		break;
498 #endif
499 	}
500 
501 	return (0);
502 }
503