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 55 struct 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 70 static int usl_sync_init(struct wsscreen *, struct usl_syncdata **, 71 struct proc *, int, int, int); 72 static void usl_sync_done(struct usl_syncdata *); 73 static int usl_sync_check(void *); 74 static int usl_sync_check_sig(struct usl_syncdata *, int, int); 75 static struct usl_syncdata *usl_sync_get(struct wsscreen *); 76 77 static int usl_detachproc(void *, int, void (*)(void *, int, int), void *); 78 static int usl_detachack(struct usl_syncdata *, int); 79 static void usl_detachtimeout(void *); 80 static int usl_attachproc(void *, int, void (*)(void *, int, int), void *); 81 static int usl_attachack(struct usl_syncdata *, int); 82 static void usl_attachtimeout(void *); 83 84 static 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 95 static int wscompat_usl_synctimeout = WSCOMPAT_USL_SYNCTIMEOUT; 96 97 static int 98 usl_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 127 static void 128 usl_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 142 static int 143 usl_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 161 static int 162 usl_sync_check(void *vsd) 163 { 164 165 struct usl_syncdata *sd = vsd; 166 return usl_sync_check_sig(sd, 0, 0); 167 } 168 169 static struct usl_syncdata * 170 usl_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 179 static int 180 usl_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 205 static int 206 usl_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 222 static void 223 usl_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 242 static int 243 usl_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 261 static int 262 usl_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 278 static void 279 usl_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 298 int 299 wsdisplay_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 369 int 370 wsdisplay_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