1 /* $NetBSD: wsdisplay_compat_usl.c,v 1.19 2002/09/23 05:51:17 simonb 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.19 2002/09/23 05:51:17 simonb 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 350 int 351 wsdisplay_usl_ioctl2(struct wsdisplay_softc *sc, struct wsscreen *scr, 352 u_long cmd, caddr_t data, int flag, struct proc *p) 353 { 354 int intarg, res; 355 u_long req; 356 void *arg; 357 struct usl_syncdata *sd; 358 struct wskbd_bell_data bd; 359 360 switch (cmd) { 361 case VT_SETMODE: 362 #define newmode ((struct vt_mode *)data) 363 if (newmode->mode == VT_PROCESS) { 364 res = usl_sync_init(scr, &sd, p, newmode->acqsig, 365 newmode->relsig, newmode->frsig); 366 if (res) 367 return (res); 368 } else { 369 sd = usl_sync_get(scr); 370 if (sd) 371 usl_sync_done(sd); 372 } 373 #undef newmode 374 return (0); 375 case VT_GETMODE: 376 #define cmode ((struct vt_mode *)data) 377 sd = usl_sync_get(scr); 378 if (sd) { 379 cmode->mode = VT_PROCESS; 380 cmode->relsig = sd->s_relsig; 381 cmode->acqsig = sd->s_acqsig; 382 cmode->frsig = sd->s_frsig; 383 } else 384 cmode->mode = VT_AUTO; 385 #undef cmode 386 return (0); 387 case VT_RELDISP: 388 #define d (*(int *)data) 389 sd = usl_sync_get(scr); 390 if (!sd) 391 return (EINVAL); 392 switch (d) { 393 case VT_FALSE: 394 case VT_TRUE: 395 return (usl_detachack(sd, (d == VT_TRUE))); 396 case VT_ACKACQ: 397 return (usl_attachack(sd, 1)); 398 default: 399 return (EINVAL); 400 } 401 #undef d 402 403 case KDENABIO: 404 if (suser(p->p_ucred, &p->p_acflag) || securelevel > 1) 405 return (EPERM); 406 /* FALLTHRU */ 407 case KDDISABIO: 408 #if defined(__i386__) 409 #if defined(COMPAT_10) || defined(COMPAT_11) || defined(COMPAT_FREEBSD) 410 { 411 struct trapframe *fp = (struct trapframe *)p->p_md.md_regs; 412 if (cmd == KDENABIO) 413 fp->tf_eflags |= PSL_IOPL; 414 else 415 fp->tf_eflags &= ~PSL_IOPL; 416 } 417 #endif 418 #endif 419 return (0); 420 case KDSETRAD: 421 /* XXX ignore for now */ 422 return (0); 423 424 default: 425 return (EPASSTHROUGH); 426 427 /* 428 * the following are converted to wsdisplay ioctls 429 */ 430 case KDSETMODE: 431 req = WSDISPLAYIO_SMODE; 432 #define d (*(int *)data) 433 switch (d) { 434 case KD_GRAPHICS: 435 intarg = WSDISPLAYIO_MODE_MAPPED; 436 break; 437 case KD_TEXT: 438 intarg = WSDISPLAYIO_MODE_EMUL; 439 break; 440 default: 441 return (EINVAL); 442 } 443 #undef d 444 arg = &intarg; 445 break; 446 case KDMKTONE: 447 req = WSKBDIO_COMPLEXBELL; 448 #define d (*(int *)data) 449 if (d) { 450 #define PCVT_SYSBEEPF 1193182 451 if (d >> 16) { 452 bd.which = WSKBD_BELL_DOPERIOD; 453 bd.period = d >> 16; /* ms */ 454 } 455 else 456 bd.which = 0; 457 if (d & 0xffff) { 458 bd.which |= WSKBD_BELL_DOPITCH; 459 bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */ 460 } 461 } else 462 bd.which = 0; /* default */ 463 #undef d 464 arg = &bd; 465 break; 466 case KDSETLED: 467 req = WSKBDIO_SETLEDS; 468 intarg = 0; 469 #define d (*(int *)data) 470 if (d & LED_CAP) 471 intarg |= WSKBD_LED_CAPS; 472 if (d & LED_NUM) 473 intarg |= WSKBD_LED_NUM; 474 if (d & LED_SCR) 475 intarg |= WSKBD_LED_SCROLL; 476 #undef d 477 arg = &intarg; 478 break; 479 case KDGETLED: 480 req = WSKBDIO_GETLEDS; 481 arg = &intarg; 482 break; 483 #ifdef WSDISPLAY_COMPAT_RAWKBD 484 case KDSKBMODE: 485 req = WSKBDIO_SETMODE; 486 switch (*(int *)data) { 487 case K_RAW: 488 intarg = WSKBD_RAW; 489 break; 490 case K_XLATE: 491 intarg = WSKBD_TRANSLATED; 492 break; 493 default: 494 return (EINVAL); 495 } 496 arg = &intarg; 497 break; 498 case KDGKBMODE: 499 req = WSKBDIO_GETMODE; 500 arg = &intarg; 501 break; 502 #endif 503 } 504 505 res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, p); 506 if (res != EPASSTHROUGH) 507 return (res); 508 509 switch (cmd) { 510 case KDGETLED: 511 #define d (*(int *)data) 512 d = 0; 513 if (intarg & WSKBD_LED_CAPS) 514 d |= LED_CAP; 515 if (intarg & WSKBD_LED_NUM) 516 d |= LED_NUM; 517 if (intarg & WSKBD_LED_SCROLL) 518 d |= LED_SCR; 519 #undef d 520 break; 521 #ifdef WSDISPLAY_COMPAT_RAWKBD 522 case KDGKBMODE: 523 *(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE); 524 break; 525 #endif 526 } 527 528 return (0); 529 } 530