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