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