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