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
47 int wsdisplaydebug = 0;
48 #else
49 #define DPRINTF(x)
50 #endif
51
52 struct 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
67 int usl_sync_init(struct wsscreen *, struct usl_syncdata **,
68 struct process *, 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
usl_sync_init(struct wsscreen * scr,struct usl_syncdata ** sdp,struct process * pr,int acqsig,int relsig,int frsig)95 usl_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
125 void
usl_sync_done(struct usl_syncdata * sd)126 usl_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
140 int
usl_sync_check(struct usl_syncdata * sd)141 usl_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
150 struct usl_syncdata *
usl_sync_get(struct wsscreen * scr)151 usl_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
160 int
usl_detachproc(void * cookie,int waitok,void (* callback)(void *,int,int),void * cbarg)161 usl_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
187 int
usl_detachack(struct usl_syncdata * sd,int ack)188 usl_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
204 void
usl_detachtimeout(void * arg)205 usl_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
224 int
usl_attachproc(void * cookie,int waitok,void (* callback)(void *,int,int),void * cbarg)225 usl_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
246 int
usl_attachack(struct usl_syncdata * sd,int ack)247 usl_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
263 void
usl_attachtimeout(void * arg)264 usl_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
283 int
wsdisplay_usl_ioctl1(struct wsdisplay_softc * sc,u_long cmd,caddr_t data,int flag,struct proc * p)284 wsdisplay_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
336 int
wsdisplay_usl_ioctl2(struct wsdisplay_softc * sc,struct wsscreen * scr,u_long cmd,caddr_t data,int flag,struct proc * p)337 wsdisplay_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