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