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