xref: /openbsd-src/sys/dev/wscons/wsdisplay_compat_usl.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /* $OpenBSD: wsdisplay_compat_usl.c,v 1.20 2008/09/10 14:01:23 blambert 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/ioctl.h>
34 #include <sys/kernel.h>
35 #include <sys/proc.h>
36 #include <sys/signalvar.h>
37 #include <sys/malloc.h>
38 #include <sys/errno.h>
39 
40 #include <dev/wscons/wsconsio.h>
41 #include <dev/wscons/wsdisplayvar.h>
42 #include <dev/wscons/wscons_callbacks.h>
43 #include <dev/wscons/wsdisplay_usl_io.h>
44 
45 #ifdef WSDISPLAY_DEBUG
46 #define DPRINTF(x)	 if (wsdisplaydebug) printf x
47 int	wsdisplaydebug = 0;
48 #else
49 #define DPRINTF(x)
50 #endif
51 
52 struct usl_syncdata {
53 	struct wsscreen *s_scr;
54 	struct proc *s_proc;
55 	pid_t s_pid;
56 	int s_flags;
57 #define SF_DETACHPENDING 1
58 #define SF_ATTACHPENDING 2
59 	int s_acqsig, s_relsig;
60 	int s_frsig; /* unused */
61 	void (*s_callback)(void *, int, int);
62 	void *s_cbarg;
63 	struct timeout s_attach_ch;
64 	struct timeout s_detach_ch;
65 };
66 
67 int usl_sync_init(struct wsscreen *, struct usl_syncdata **,
68 		       struct proc *, int, int, int);
69 void usl_sync_done(struct usl_syncdata *);
70 int usl_sync_check(struct usl_syncdata *);
71 struct usl_syncdata *usl_sync_get(struct wsscreen *);
72 
73 int usl_detachproc(void *, int, void (*)(void *, int, int), void *);
74 int usl_detachack(struct usl_syncdata *, int);
75 void usl_detachtimeout(void *);
76 int usl_attachproc(void *, int, void (*)(void *, int, int), void *);
77 int usl_attachack(struct usl_syncdata *, int);
78 void usl_attachtimeout(void *);
79 
80 static const struct wscons_syncops usl_syncops = {
81 	usl_detachproc,
82 	usl_attachproc,
83 #define _usl_sync_check ((int (*)(void *))usl_sync_check)
84 	_usl_sync_check,
85 #define _usl_sync_destroy ((void (*)(void *))usl_sync_done)
86 	_usl_sync_destroy
87 };
88 
89 #ifndef WSCOMPAT_USL_SYNCTIMEOUT
90 #define WSCOMPAT_USL_SYNCTIMEOUT 5 /* seconds */
91 #endif
92 static int wscompat_usl_synctimeout = WSCOMPAT_USL_SYNCTIMEOUT;
93 
94 int
95 usl_sync_init(scr, sdp, p, acqsig, relsig, frsig)
96 	struct wsscreen *scr;
97 	struct usl_syncdata **sdp;
98 	struct proc *p;
99 	int acqsig, relsig, frsig;
100 {
101 	struct usl_syncdata *sd;
102 	int res;
103 
104 	if (acqsig <= 0 || acqsig >= NSIG || relsig <= 0 || relsig >= NSIG ||
105 	    frsig <= 0 || frsig >= NSIG)
106 		return (EINVAL);
107 	sd = malloc(sizeof(struct usl_syncdata), M_DEVBUF, M_NOWAIT);
108 	if (!sd)
109 		return (ENOMEM);
110 	sd->s_scr = scr;
111 	sd->s_proc = p;
112 	sd->s_pid = p->p_pid;
113 	sd->s_flags = 0;
114 	sd->s_acqsig = acqsig;
115 	sd->s_relsig = relsig;
116 	sd->s_frsig = frsig;
117 	timeout_set(&sd->s_attach_ch, usl_attachtimeout, sd);
118 	timeout_set(&sd->s_detach_ch, usl_detachtimeout, sd);
119 	res = wsscreen_attach_sync(scr, &usl_syncops, sd);
120 	if (res) {
121 		free(sd, M_DEVBUF);
122 		return (res);
123 	}
124 	*sdp = sd;
125 	return (0);
126 }
127 
128 void
129 usl_sync_done(sd)
130 	struct usl_syncdata *sd;
131 {
132 	if (sd->s_flags & SF_DETACHPENDING) {
133 		timeout_del(&sd->s_detach_ch);
134 		(*sd->s_callback)(sd->s_cbarg, 0, 0);
135 	}
136 	if (sd->s_flags & SF_ATTACHPENDING) {
137 		timeout_del(&sd->s_attach_ch);
138 		(*sd->s_callback)(sd->s_cbarg, ENXIO, 0);
139 	}
140 	wsscreen_detach_sync(sd->s_scr);
141 	free(sd, M_DEVBUF);
142 }
143 
144 int
145 usl_sync_check(sd)
146 	struct usl_syncdata *sd;
147 {
148 	if (sd->s_proc == pfind(sd->s_pid))
149 		return (1);
150 	DPRINTF(("usl_sync_check: process %d died\n", sd->s_pid));
151 	usl_sync_done(sd);
152 	return (0);
153 }
154 
155 struct usl_syncdata *
156 usl_sync_get(scr)
157 	struct wsscreen *scr;
158 {
159 	struct usl_syncdata *sd;
160 
161 	if (wsscreen_lookup_sync(scr, &usl_syncops, (void **)&sd))
162 		return (0);
163 	return (sd);
164 }
165 
166 int
167 usl_detachproc(cookie, waitok, callback, cbarg)
168 	void *cookie;
169 	int waitok;
170 	void (*callback)(void *, int, int);
171 	void *cbarg;
172 {
173 	struct usl_syncdata *sd = cookie;
174 
175 	if (!usl_sync_check(sd))
176 		return (0);
177 
178 	/* we really need a callback */
179 	if (!callback)
180 		return (EINVAL);
181 
182 	/*
183 	 * Normally, this is called from the controlling process.
184 	 * It is supposed to reply with a VT_RELDISP ioctl(), so
185 	 * it is not useful to tsleep() here.
186 	 */
187 	sd->s_callback = callback;
188 	sd->s_cbarg = cbarg;
189 	sd->s_flags |= SF_DETACHPENDING;
190 	psignal(sd->s_proc, sd->s_relsig);
191 	timeout_add_sec(&sd->s_detach_ch, wscompat_usl_synctimeout);
192 
193 	return (EAGAIN);
194 }
195 
196 int
197 usl_detachack(sd, ack)
198 	struct usl_syncdata *sd;
199 	int ack;
200 {
201 	if (!(sd->s_flags & SF_DETACHPENDING)) {
202 		DPRINTF(("usl_detachack: not detaching\n"));
203 		return (EINVAL);
204 	}
205 
206 	timeout_del(&sd->s_detach_ch);
207 	sd->s_flags &= ~SF_DETACHPENDING;
208 
209 	if (sd->s_callback)
210 		(*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1);
211 
212 	return (0);
213 }
214 
215 void
216 usl_detachtimeout(arg)
217 	void *arg;
218 {
219 	struct usl_syncdata *sd = arg;
220 
221 	DPRINTF(("usl_detachtimeout\n"));
222 
223 	if (!(sd->s_flags & SF_DETACHPENDING)) {
224 		DPRINTF(("usl_detachtimeout: not detaching\n"));
225 		return;
226 	}
227 
228 	sd->s_flags &= ~SF_DETACHPENDING;
229 
230 	if (sd->s_callback)
231 		(*sd->s_callback)(sd->s_cbarg, EIO, 0);
232 
233 	(void) usl_sync_check(sd);
234 }
235 
236 int
237 usl_attachproc(cookie, waitok, callback, cbarg)
238 	void *cookie;
239 	int waitok;
240 	void (*callback)(void *, int, int);
241 	void *cbarg;
242 {
243 	struct usl_syncdata *sd = cookie;
244 
245 	if (!usl_sync_check(sd))
246 		return (0);
247 
248 	/* we really need a callback */
249 	if (!callback)
250 		return (EINVAL);
251 
252 	sd->s_callback = callback;
253 	sd->s_cbarg = cbarg;
254 	sd->s_flags |= SF_ATTACHPENDING;
255 	psignal(sd->s_proc, sd->s_acqsig);
256 	timeout_add_sec(&sd->s_attach_ch, wscompat_usl_synctimeout);
257 
258 	return (EAGAIN);
259 }
260 
261 int
262 usl_attachack(sd, ack)
263 	struct usl_syncdata *sd;
264 	int ack;
265 {
266 	if (!(sd->s_flags & SF_ATTACHPENDING)) {
267 		DPRINTF(("usl_attachack: not attaching\n"));
268 		return (EINVAL);
269 	}
270 
271 	timeout_del(&sd->s_attach_ch);
272 	sd->s_flags &= ~SF_ATTACHPENDING;
273 
274 	if (sd->s_callback)
275 		(*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1);
276 
277 	return (0);
278 }
279 
280 void
281 usl_attachtimeout(arg)
282 	void *arg;
283 {
284 	struct usl_syncdata *sd = arg;
285 
286 	DPRINTF(("usl_attachtimeout\n"));
287 
288 	if (!(sd->s_flags & SF_ATTACHPENDING)) {
289 		DPRINTF(("usl_attachtimeout: not attaching\n"));
290 		return;
291 	}
292 
293 	sd->s_flags &= ~SF_ATTACHPENDING;
294 
295 	if (sd->s_callback)
296 		(*sd->s_callback)(sd->s_cbarg, EIO, 0);
297 
298 	(void) usl_sync_check(sd);
299 }
300 
301 int
302 wsdisplay_usl_ioctl1(sc, cmd, data, flag, p)
303 	struct wsdisplay_softc *sc;
304 	u_long cmd;
305 	caddr_t data;
306 	int flag;
307 	struct proc *p;
308 {
309 	int idx, maxidx;
310 
311 	switch (cmd) {
312 	    case VT_OPENQRY:
313 		maxidx = wsdisplay_maxscreenidx(sc);
314 		for (idx = 0; idx <= maxidx; idx++) {
315 			if (wsdisplay_screenstate(sc, idx) == 0) {
316 				*(int *)data = idx + 1;
317 				return (0);
318 			}
319 		}
320 		return (ENXIO);
321 	    case VT_GETACTIVE:
322 		idx = wsdisplay_getactivescreen(sc);
323 		*(int *)data = idx + 1;
324 		return (0);
325 	    case VT_ACTIVATE:
326 		idx = *(int *)data - 1;
327 		if (idx < 0)
328 			return (EINVAL);
329 		return (wsdisplay_switch((struct device *)sc, idx, 1));
330 	    case VT_WAITACTIVE:
331 		idx = *(int *)data - 1;
332 		if (idx < 0)
333 			return (EINVAL);
334 		return (wsscreen_switchwait(sc, idx));
335 	    case VT_GETSTATE:
336 #define ss ((struct vt_stat *)data)
337 		idx = wsdisplay_getactivescreen(sc);
338 		ss->v_active = idx + 1;
339 		ss->v_state = 0;
340 		maxidx = wsdisplay_maxscreenidx(sc);
341 		for (idx = 0; idx <= maxidx; idx++)
342 			if (wsdisplay_screenstate(sc, idx) == EBUSY)
343 				ss->v_state |= (1 << (idx + 1));
344 #undef ss
345 		return (0);
346 
347 #ifdef WSDISPLAY_COMPAT_PCVT
348 	    case VGAPCVTID:
349 #define id ((struct pcvtid *)data)
350 		strlcpy(id->name, "pcvt", sizeof id->name);
351 		id->rmajor = 3;
352 		id->rminor = 32;
353 #undef id
354 		return (0);
355 #endif
356 #ifdef WSDISPLAY_COMPAT_SYSCONS
357 	    case CONS_GETVERS:
358 		*(int *)data = 0x200;    /* version 2.0 */
359 		return (0);
360 #endif
361 
362 	    default:
363 		return (-1);
364 	}
365 
366 	return (0);
367 }
368 
369 int
370 wsdisplay_usl_ioctl2(sc, scr, cmd, data, flag, p)
371 	struct wsdisplay_softc *sc;
372 	struct wsscreen *scr;
373 	u_long cmd;
374 	caddr_t data;
375 	int flag;
376 	struct proc *p;
377 {
378 	int intarg, res;
379 	u_long req;
380 	void *arg;
381 	struct usl_syncdata *sd;
382 	struct wskbd_bell_data bd;
383 
384 	switch (cmd) {
385 	    case VT_SETMODE:
386 #define newmode ((struct vt_mode *)data)
387 		if (newmode->mode == VT_PROCESS) {
388 			res = usl_sync_init(scr, &sd, p, newmode->acqsig,
389 					    newmode->relsig, newmode->frsig);
390 			if (res)
391 				return (res);
392 		} else {
393 			sd = usl_sync_get(scr);
394 			if (sd)
395 				usl_sync_done(sd);
396 		}
397 #undef newmode
398 		return (0);
399 	    case VT_GETMODE:
400 #define cmode ((struct vt_mode *)data)
401 		sd = usl_sync_get(scr);
402 		if (sd) {
403 			cmode->mode = VT_PROCESS;
404 			cmode->relsig = sd->s_relsig;
405 			cmode->acqsig = sd->s_acqsig;
406 			cmode->frsig = sd->s_frsig;
407 		} else
408 			cmode->mode = VT_AUTO;
409 #undef cmode
410 		return (0);
411 	    case VT_RELDISP:
412 #define d (*(int *)data)
413 		sd = usl_sync_get(scr);
414 		if (!sd)
415 			return (EINVAL);
416 		switch (d) {
417 		    case VT_FALSE:
418 		    case VT_TRUE:
419 			return (usl_detachack(sd, (d == VT_TRUE)));
420 		    case VT_ACKACQ:
421 			return (usl_attachack(sd, 1));
422 		    default:
423 			return (EINVAL);
424 		}
425 #undef d
426 		return (0);
427 
428 #if defined(__i386__)
429 	    case KDENABIO:
430 		if (suser(p, 0) || securelevel > 0)
431 			return (EPERM);
432 		/* FALLTHROUGH */
433 	    case KDDISABIO:
434 #if defined(COMPAT_FREEBSD)
435 		{
436 		struct trapframe *fp = (struct trapframe *)p->p_md.md_regs;
437 		extern struct emul emul_freebsd_aout;
438 		extern struct emul emul_freebsd_elf;
439 
440 		if (p->p_emul == &emul_freebsd_aout ||
441 		    p->p_emul == &emul_freebsd_elf) {
442 			if (cmd == KDENABIO)
443 				fp->tf_eflags |= PSL_IOPL;
444 			else
445 				fp->tf_eflags &= ~PSL_IOPL;
446 			}
447 		}
448 #endif
449 		return (0);
450 #else
451 	    case KDENABIO:
452 	    case KDDISABIO:
453 		/*
454 		 * This is a lie, but non-x86 platforms are not supposed to
455 		 * issue these ioctls anyway.
456 		 */
457 		return (0);
458 #endif
459 	    case KDSETRAD:
460 		/* XXX ignore for now */
461 		return (0);
462 
463 	    default:
464 		return (-1);
465 
466 	    /*
467 	     * the following are converted to wsdisplay ioctls
468 	     */
469 	    case KDSETMODE:
470 		req = WSDISPLAYIO_SMODE;
471 #define d (*(int *)data)
472 		switch (d) {
473 		    case KD_GRAPHICS:
474 			intarg = WSDISPLAYIO_MODE_MAPPED;
475 			break;
476 		    case KD_TEXT:
477 			intarg = WSDISPLAYIO_MODE_EMUL;
478 			break;
479 		    default:
480 			return (EINVAL);
481 		}
482 #undef d
483 		arg = &intarg;
484 		break;
485 	    case KDMKTONE:
486 		req = WSKBDIO_COMPLEXBELL;
487 #define d (*(int *)data)
488 		if (d) {
489 #define PCVT_SYSBEEPF	1193182
490 			if (d >> 16) {
491 				bd.which = WSKBD_BELL_DOPERIOD;
492 			bd.period = d >> 16; /* ms */
493 			}
494 			else
495 				bd.which = 0;
496 			if (d & 0xffff) {
497 				bd.which |= WSKBD_BELL_DOPITCH;
498 				bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */
499 			}
500 		} else
501 			bd.which = 0; /* default */
502 #undef d
503 		arg = &bd;
504 		break;
505 	    case KDSETLED:
506 		req = WSKBDIO_SETLEDS;
507 		intarg = 0;
508 #define d (*(int *)data)
509 		if (d & LED_CAP)
510 			intarg |= WSKBD_LED_CAPS;
511 		if (d & LED_NUM)
512 			intarg |= WSKBD_LED_NUM;
513 		if (d & LED_SCR)
514 			intarg |= WSKBD_LED_SCROLL;
515 #undef d
516 		arg = &intarg;
517 		break;
518 	    case KDGETLED:
519 		req = WSKBDIO_GETLEDS;
520 		arg = &intarg;
521 		break;
522 #ifdef WSDISPLAY_COMPAT_RAWKBD
523 	    case KDSKBMODE:
524 		req = WSKBDIO_SETMODE;
525 		switch (*(int *)data) {
526 		    case K_RAW:
527 			intarg = WSKBD_RAW;
528 			break;
529 		    case K_XLATE:
530 			intarg = WSKBD_TRANSLATED;
531 			break;
532 		    default:
533 			return (EINVAL);
534 		}
535 		arg = &intarg;
536 		break;
537 	    case KDGKBMODE:
538 		req = WSKBDIO_GETMODE;
539 		arg = &intarg;
540 		break;
541 #endif
542 	}
543 
544 	res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, p);
545 	if (res)
546 		return (res);
547 
548 	switch (cmd) {
549 	    case KDGETLED:
550 #define d (*(int *)data)
551 		d = 0;
552 		if (intarg & WSKBD_LED_CAPS)
553 			d |= LED_CAP;
554 		if (intarg & WSKBD_LED_NUM)
555 			d |= LED_NUM;
556 		if (intarg & WSKBD_LED_SCROLL)
557 			d |= LED_SCR;
558 #undef d
559 		break;
560 #ifdef WSDISPLAY_COMPAT_RAWKBD
561 	    case KDGKBMODE:
562 		*(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE);
563 		break;
564 #endif
565 	}
566 
567 	return (0);
568 }
569