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