1 /* $NetBSD: wsdisplay_compat_usl.c,v 1.25 2004/04/23 21:29:16 itojun 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.25 2004/04/23 21:29:16 itojun 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 proc *p) 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 idx = *(int *)data - 1; 304 if (idx < 0) 305 return (EINVAL); 306 return (wsdisplay_switch((struct device *)sc, idx, 1)); 307 case VT_WAITACTIVE: 308 idx = *(int *)data - 1; 309 if (idx < 0) 310 return (EINVAL); 311 return (wsscreen_switchwait(sc, idx)); 312 case VT_GETSTATE: 313 #define ss ((struct vt_stat *)data) 314 idx = wsdisplay_getactivescreen(sc); 315 ss->v_active = idx + 1; 316 ss->v_state = 0; 317 maxidx = wsdisplay_maxscreenidx(sc); 318 for (idx = 0; idx <= maxidx; idx++) 319 if (wsdisplay_screenstate(sc, idx) == EBUSY) 320 ss->v_state |= (1 << (idx + 1)); 321 #undef s 322 return (0); 323 324 #ifdef WSDISPLAY_COMPAT_PCVT 325 case VGAPCVTID: 326 #define id ((struct pcvtid *)data) 327 strlcpy(id->name, "pcvt", sizeof(id->name)); 328 id->rmajor = 3; 329 id->rminor = 32; 330 #undef id 331 return (0); 332 #endif 333 #ifdef WSDISPLAY_COMPAT_SYSCONS 334 case CONS_GETVERS: 335 *(int *)data = 0x200; /* version 2.0 */ 336 return (0); 337 #endif 338 339 default: 340 return (EPASSTHROUGH); 341 } 342 } 343 344 int 345 wsdisplay_usl_ioctl2(struct wsdisplay_softc *sc, struct wsscreen *scr, 346 u_long cmd, caddr_t data, int flag, struct proc *p) 347 { 348 int intarg, res; 349 u_long req; 350 void *arg; 351 struct usl_syncdata *sd; 352 struct wskbd_bell_data bd; 353 354 switch (cmd) { 355 case VT_SETMODE: 356 #define newmode ((struct vt_mode *)data) 357 if (newmode->mode == VT_PROCESS) { 358 res = usl_sync_init(scr, &sd, p, newmode->acqsig, 359 newmode->relsig, newmode->frsig); 360 if (res) 361 return (res); 362 } else { 363 sd = usl_sync_get(scr); 364 if (sd) 365 usl_sync_done(sd); 366 } 367 #undef newmode 368 return (0); 369 case VT_GETMODE: 370 #define cmode ((struct vt_mode *)data) 371 sd = usl_sync_get(scr); 372 if (sd) { 373 cmode->mode = VT_PROCESS; 374 cmode->relsig = sd->s_relsig; 375 cmode->acqsig = sd->s_acqsig; 376 cmode->frsig = sd->s_frsig; 377 } else 378 cmode->mode = VT_AUTO; 379 #undef cmode 380 return (0); 381 case VT_RELDISP: 382 #define d (*(int *)data) 383 sd = usl_sync_get(scr); 384 if (!sd) 385 return (EINVAL); 386 switch (d) { 387 case VT_FALSE: 388 case VT_TRUE: 389 return (usl_detachack(sd, (d == VT_TRUE))); 390 case VT_ACKACQ: 391 return (usl_attachack(sd, 1)); 392 default: 393 return (EINVAL); 394 } 395 #undef d 396 397 case KDENABIO: 398 if (suser(p->p_ucred, &p->p_acflag) || securelevel > 1) 399 return (EPERM); 400 /* FALLTHRU */ 401 case KDDISABIO: 402 #if defined(__i386__) 403 #if defined(COMPAT_10) || defined(COMPAT_11) || defined(COMPAT_FREEBSD) 404 { 405 /* XXX NJWLWP */ 406 struct trapframe *fp = (struct trapframe *)curlwp->l_md.md_regs; 407 if (cmd == KDENABIO) 408 fp->tf_eflags |= PSL_IOPL; 409 else 410 fp->tf_eflags &= ~PSL_IOPL; 411 } 412 #endif 413 #endif 414 return (0); 415 case KDSETRAD: 416 /* XXX ignore for now */ 417 return (0); 418 419 default: 420 return (EPASSTHROUGH); 421 422 /* 423 * the following are converted to wsdisplay ioctls 424 */ 425 case KDSETMODE: 426 req = WSDISPLAYIO_SMODE; 427 #define d (*(int *)data) 428 switch (d) { 429 case KD_GRAPHICS: 430 intarg = WSDISPLAYIO_MODE_MAPPED; 431 break; 432 case KD_TEXT: 433 intarg = WSDISPLAYIO_MODE_EMUL; 434 break; 435 default: 436 return (EINVAL); 437 } 438 #undef d 439 arg = &intarg; 440 break; 441 case KDMKTONE: 442 req = WSKBDIO_COMPLEXBELL; 443 #define d (*(int *)data) 444 if (d) { 445 #define PCVT_SYSBEEPF 1193182 446 if (d >> 16) { 447 bd.which = WSKBD_BELL_DOPERIOD; 448 bd.period = d >> 16; /* ms */ 449 } 450 else 451 bd.which = 0; 452 if (d & 0xffff) { 453 bd.which |= WSKBD_BELL_DOPITCH; 454 bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */ 455 } 456 } else 457 bd.which = 0; /* default */ 458 #undef d 459 arg = &bd; 460 break; 461 case KDSETLED: 462 req = WSKBDIO_SETLEDS; 463 intarg = 0; 464 #define d (*(int *)data) 465 if (d & LED_CAP) 466 intarg |= WSKBD_LED_CAPS; 467 if (d & LED_NUM) 468 intarg |= WSKBD_LED_NUM; 469 if (d & LED_SCR) 470 intarg |= WSKBD_LED_SCROLL; 471 #undef d 472 arg = &intarg; 473 break; 474 case KDGETLED: 475 req = WSKBDIO_GETLEDS; 476 arg = &intarg; 477 break; 478 #ifdef WSDISPLAY_COMPAT_RAWKBD 479 case KDSKBMODE: 480 req = WSKBDIO_SETMODE; 481 switch (*(int *)data) { 482 case K_RAW: 483 intarg = WSKBD_RAW; 484 break; 485 case K_XLATE: 486 intarg = WSKBD_TRANSLATED; 487 break; 488 default: 489 return (EINVAL); 490 } 491 arg = &intarg; 492 break; 493 case KDGKBMODE: 494 req = WSKBDIO_GETMODE; 495 arg = &intarg; 496 break; 497 #endif 498 } 499 500 res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, p); 501 if (res != EPASSTHROUGH) 502 return (res); 503 504 switch (cmd) { 505 case KDGETLED: 506 #define d (*(int *)data) 507 d = 0; 508 if (intarg & WSKBD_LED_CAPS) 509 d |= LED_CAP; 510 if (intarg & WSKBD_LED_NUM) 511 d |= LED_NUM; 512 if (intarg & WSKBD_LED_SCROLL) 513 d |= LED_SCR; 514 #undef d 515 break; 516 #ifdef WSDISPLAY_COMPAT_RAWKBD 517 case KDGKBMODE: 518 *(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE); 519 break; 520 #endif 521 } 522 523 return (0); 524 } 525