1 /* $OpenBSD: wsdisplay_compat_usl.c,v 1.22 2011/04/05 12:50:15 guenther Exp $ */ 2 /* $NetBSD: wsdisplay_compat_usl.c,v 1.12 2000/03/23 07:01:47 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 1998 6 * Matthias Drochner. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/timeout.h> 33 #include <sys/ioctl.h> 34 #include <sys/kernel.h> 35 #include <sys/proc.h> 36 #include <sys/signalvar.h> 37 #include <sys/malloc.h> 38 #include <sys/errno.h> 39 40 #include <dev/wscons/wsconsio.h> 41 #include <dev/wscons/wsdisplayvar.h> 42 #include <dev/wscons/wscons_callbacks.h> 43 #include <dev/wscons/wsdisplay_usl_io.h> 44 45 #ifdef WSDISPLAY_DEBUG 46 #define DPRINTF(x) if (wsdisplaydebug) printf x 47 int wsdisplaydebug = 0; 48 #else 49 #define DPRINTF(x) 50 #endif 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 timeout s_attach_ch; 64 struct timeout s_detach_ch; 65 }; 66 67 int usl_sync_init(struct wsscreen *, struct usl_syncdata **, 68 struct proc *, int, int, int); 69 void usl_sync_done(struct usl_syncdata *); 70 int usl_sync_check(struct usl_syncdata *); 71 struct usl_syncdata *usl_sync_get(struct wsscreen *); 72 73 int usl_detachproc(void *, int, void (*)(void *, int, int), void *); 74 int usl_detachack(struct usl_syncdata *, int); 75 void usl_detachtimeout(void *); 76 int usl_attachproc(void *, int, void (*)(void *, int, int), void *); 77 int usl_attachack(struct usl_syncdata *, int); 78 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 int 95 usl_sync_init(struct wsscreen *scr, struct usl_syncdata **sdp, struct proc *p, 96 int acqsig, int relsig, int frsig) 97 { 98 struct usl_syncdata *sd; 99 int res; 100 101 if (acqsig <= 0 || acqsig >= NSIG || relsig <= 0 || relsig >= NSIG || 102 frsig <= 0 || frsig >= NSIG) 103 return (EINVAL); 104 sd = malloc(sizeof(struct usl_syncdata), M_DEVBUF, M_NOWAIT); 105 if (!sd) 106 return (ENOMEM); 107 sd->s_scr = scr; 108 sd->s_proc = p; 109 sd->s_pid = p->p_pid; 110 sd->s_flags = 0; 111 sd->s_acqsig = acqsig; 112 sd->s_relsig = relsig; 113 sd->s_frsig = frsig; 114 timeout_set(&sd->s_attach_ch, usl_attachtimeout, sd); 115 timeout_set(&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 void 126 usl_sync_done(struct usl_syncdata *sd) 127 { 128 if (sd->s_flags & SF_DETACHPENDING) { 129 timeout_del(&sd->s_detach_ch); 130 (*sd->s_callback)(sd->s_cbarg, 0, 0); 131 } 132 if (sd->s_flags & SF_ATTACHPENDING) { 133 timeout_del(&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 int 141 usl_sync_check(struct usl_syncdata *sd) 142 { 143 if (sd->s_proc == pfind(sd->s_pid)) 144 return (1); 145 DPRINTF(("usl_sync_check: process %d died\n", sd->s_pid)); 146 usl_sync_done(sd); 147 return (0); 148 } 149 150 struct usl_syncdata * 151 usl_sync_get(struct wsscreen *scr) 152 { 153 struct usl_syncdata *sd; 154 155 if (wsscreen_lookup_sync(scr, &usl_syncops, (void **)&sd)) 156 return (0); 157 return (sd); 158 } 159 160 int 161 usl_detachproc(void *cookie, int waitok, void (*callback)(void *, int, int), 162 void *cbarg) 163 { 164 struct usl_syncdata *sd = cookie; 165 166 if (!usl_sync_check(sd)) 167 return (0); 168 169 /* we really need a callback */ 170 if (!callback) 171 return (EINVAL); 172 173 /* 174 * Normally, this is called from the controlling process. 175 * It is supposed to reply with a VT_RELDISP ioctl(), so 176 * it is not useful to tsleep() here. 177 */ 178 sd->s_callback = callback; 179 sd->s_cbarg = cbarg; 180 sd->s_flags |= SF_DETACHPENDING; 181 psignal(sd->s_proc, sd->s_relsig); 182 timeout_add_sec(&sd->s_detach_ch, wscompat_usl_synctimeout); 183 184 return (EAGAIN); 185 } 186 187 int 188 usl_detachack(struct usl_syncdata *sd, int ack) 189 { 190 if (!(sd->s_flags & SF_DETACHPENDING)) { 191 DPRINTF(("usl_detachack: not detaching\n")); 192 return (EINVAL); 193 } 194 195 timeout_del(&sd->s_detach_ch); 196 sd->s_flags &= ~SF_DETACHPENDING; 197 198 if (sd->s_callback) 199 (*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1); 200 201 return (0); 202 } 203 204 void 205 usl_detachtimeout(void *arg) 206 { 207 struct usl_syncdata *sd = arg; 208 209 DPRINTF(("usl_detachtimeout\n")); 210 211 if (!(sd->s_flags & SF_DETACHPENDING)) { 212 DPRINTF(("usl_detachtimeout: not detaching\n")); 213 return; 214 } 215 216 sd->s_flags &= ~SF_DETACHPENDING; 217 218 if (sd->s_callback) 219 (*sd->s_callback)(sd->s_cbarg, EIO, 0); 220 221 (void) usl_sync_check(sd); 222 } 223 224 int 225 usl_attachproc(void *cookie, int waitok, void (*callback)(void *, int, int), 226 void *cbarg) 227 { 228 struct usl_syncdata *sd = cookie; 229 230 if (!usl_sync_check(sd)) 231 return (0); 232 233 /* we really need a callback */ 234 if (!callback) 235 return (EINVAL); 236 237 sd->s_callback = callback; 238 sd->s_cbarg = cbarg; 239 sd->s_flags |= SF_ATTACHPENDING; 240 psignal(sd->s_proc, sd->s_acqsig); 241 timeout_add_sec(&sd->s_attach_ch, wscompat_usl_synctimeout); 242 243 return (EAGAIN); 244 } 245 246 int 247 usl_attachack(struct usl_syncdata *sd, int ack) 248 { 249 if (!(sd->s_flags & SF_ATTACHPENDING)) { 250 DPRINTF(("usl_attachack: not attaching\n")); 251 return (EINVAL); 252 } 253 254 timeout_del(&sd->s_attach_ch); 255 sd->s_flags &= ~SF_ATTACHPENDING; 256 257 if (sd->s_callback) 258 (*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1); 259 260 return (0); 261 } 262 263 void 264 usl_attachtimeout(void *arg) 265 { 266 struct usl_syncdata *sd = arg; 267 268 DPRINTF(("usl_attachtimeout\n")); 269 270 if (!(sd->s_flags & SF_ATTACHPENDING)) { 271 DPRINTF(("usl_attachtimeout: not attaching\n")); 272 return; 273 } 274 275 sd->s_flags &= ~SF_ATTACHPENDING; 276 277 if (sd->s_callback) 278 (*sd->s_callback)(sd->s_cbarg, EIO, 0); 279 280 (void) usl_sync_check(sd); 281 } 282 283 int 284 wsdisplay_usl_ioctl1(struct wsdisplay_softc *sc, u_long cmd, caddr_t data, 285 int flag, struct proc *p) 286 { 287 int idx, maxidx; 288 289 switch (cmd) { 290 case VT_OPENQRY: 291 maxidx = wsdisplay_maxscreenidx(sc); 292 for (idx = 0; idx <= maxidx; idx++) { 293 if (wsdisplay_screenstate(sc, idx) == 0) { 294 *(int *)data = idx + 1; 295 return (0); 296 } 297 } 298 return (ENXIO); 299 case VT_GETACTIVE: 300 idx = wsdisplay_getactivescreen(sc); 301 *(int *)data = idx + 1; 302 return (0); 303 case VT_ACTIVATE: 304 idx = *(int *)data - 1; 305 if (idx < 0) 306 return (EINVAL); 307 return (wsdisplay_switch((struct device *)sc, idx, 1)); 308 case VT_WAITACTIVE: 309 idx = *(int *)data - 1; 310 if (idx < 0) 311 return (EINVAL); 312 return (wsscreen_switchwait(sc, idx)); 313 case VT_GETSTATE: 314 #define ss ((struct vt_stat *)data) 315 idx = wsdisplay_getactivescreen(sc); 316 ss->v_active = idx + 1; 317 ss->v_state = 0; 318 maxidx = wsdisplay_maxscreenidx(sc); 319 for (idx = 0; idx <= maxidx; idx++) 320 if (wsdisplay_screenstate(sc, idx) == EBUSY) 321 ss->v_state |= (1 << (idx + 1)); 322 #undef ss 323 return (0); 324 325 #ifdef WSDISPLAY_COMPAT_PCVT 326 case VGAPCVTID: 327 #define id ((struct pcvtid *)data) 328 strlcpy(id->name, "pcvt", sizeof id->name); 329 id->rmajor = 3; 330 id->rminor = 32; 331 #undef id 332 return (0); 333 #endif 334 #ifdef WSDISPLAY_COMPAT_SYSCONS 335 case CONS_GETVERS: 336 *(int *)data = 0x200; /* version 2.0 */ 337 return (0); 338 #endif 339 340 default: 341 return (-1); 342 } 343 344 return (0); 345 } 346 347 int 348 wsdisplay_usl_ioctl2(struct wsdisplay_softc *sc, struct wsscreen *scr, 349 u_long cmd, caddr_t data, int flag, struct proc *p) 350 { 351 int intarg, res; 352 u_long req; 353 void *arg; 354 struct usl_syncdata *sd; 355 struct wskbd_bell_data bd; 356 357 switch (cmd) { 358 case VT_SETMODE: 359 #define newmode ((struct vt_mode *)data) 360 if (newmode->mode == VT_PROCESS) { 361 res = usl_sync_init(scr, &sd, p, newmode->acqsig, 362 newmode->relsig, newmode->frsig); 363 if (res) 364 return (res); 365 } else { 366 sd = usl_sync_get(scr); 367 if (sd) 368 usl_sync_done(sd); 369 } 370 #undef newmode 371 return (0); 372 case VT_GETMODE: 373 #define cmode ((struct vt_mode *)data) 374 sd = usl_sync_get(scr); 375 if (sd) { 376 cmode->mode = VT_PROCESS; 377 cmode->relsig = sd->s_relsig; 378 cmode->acqsig = sd->s_acqsig; 379 cmode->frsig = sd->s_frsig; 380 } else 381 cmode->mode = VT_AUTO; 382 #undef cmode 383 return (0); 384 case VT_RELDISP: 385 #define d (*(int *)data) 386 sd = usl_sync_get(scr); 387 if (!sd) 388 return (EINVAL); 389 switch (d) { 390 case VT_FALSE: 391 case VT_TRUE: 392 return (usl_detachack(sd, (d == VT_TRUE))); 393 case VT_ACKACQ: 394 return (usl_attachack(sd, 1)); 395 default: 396 return (EINVAL); 397 } 398 #undef d 399 return (0); 400 401 #if defined(__i386__) 402 case KDENABIO: 403 if (suser(p, 0) || securelevel > 0) 404 return (EPERM); 405 /* FALLTHROUGH */ 406 case KDDISABIO: 407 return (0); 408 #else 409 case KDENABIO: 410 case KDDISABIO: 411 /* 412 * This is a lie, but non-x86 platforms are not supposed to 413 * issue these ioctls anyway. 414 */ 415 return (0); 416 #endif 417 case KDSETRAD: 418 /* XXX ignore for now */ 419 return (0); 420 421 default: 422 return (-1); 423 424 /* 425 * the following are converted to wsdisplay ioctls 426 */ 427 case KDSETMODE: 428 req = WSDISPLAYIO_SMODE; 429 #define d (*(int *)data) 430 switch (d) { 431 case KD_GRAPHICS: 432 intarg = WSDISPLAYIO_MODE_MAPPED; 433 break; 434 case KD_TEXT: 435 intarg = WSDISPLAYIO_MODE_EMUL; 436 break; 437 default: 438 return (EINVAL); 439 } 440 #undef d 441 arg = &intarg; 442 break; 443 case KDMKTONE: 444 req = WSKBDIO_COMPLEXBELL; 445 #define d (*(int *)data) 446 if (d) { 447 #define PCVT_SYSBEEPF 1193182 448 if (d >> 16) { 449 bd.which = WSKBD_BELL_DOPERIOD; 450 bd.period = d >> 16; /* ms */ 451 } 452 else 453 bd.which = 0; 454 if (d & 0xffff) { 455 bd.which |= WSKBD_BELL_DOPITCH; 456 bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */ 457 } 458 } else 459 bd.which = 0; /* default */ 460 #undef d 461 arg = &bd; 462 break; 463 case KDSETLED: 464 req = WSKBDIO_SETLEDS; 465 intarg = 0; 466 #define d (*(int *)data) 467 if (d & LED_CAP) 468 intarg |= WSKBD_LED_CAPS; 469 if (d & LED_NUM) 470 intarg |= WSKBD_LED_NUM; 471 if (d & LED_SCR) 472 intarg |= WSKBD_LED_SCROLL; 473 #undef d 474 arg = &intarg; 475 break; 476 case KDGETLED: 477 req = WSKBDIO_GETLEDS; 478 arg = &intarg; 479 break; 480 #ifdef WSDISPLAY_COMPAT_RAWKBD 481 case KDSKBMODE: 482 req = WSKBDIO_SETMODE; 483 switch (*(int *)data) { 484 case K_RAW: 485 intarg = WSKBD_RAW; 486 break; 487 case K_XLATE: 488 intarg = WSKBD_TRANSLATED; 489 break; 490 default: 491 return (EINVAL); 492 } 493 arg = &intarg; 494 break; 495 case KDGKBMODE: 496 req = WSKBDIO_GETMODE; 497 arg = &intarg; 498 break; 499 #endif 500 } 501 502 res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, p); 503 if (res) 504 return (res); 505 506 switch (cmd) { 507 case KDGETLED: 508 #define d (*(int *)data) 509 d = 0; 510 if (intarg & WSKBD_LED_CAPS) 511 d |= LED_CAP; 512 if (intarg & WSKBD_LED_NUM) 513 d |= LED_NUM; 514 if (intarg & WSKBD_LED_SCROLL) 515 d |= LED_SCR; 516 #undef d 517 break; 518 #ifdef WSDISPLAY_COMPAT_RAWKBD 519 case KDGKBMODE: 520 *(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE); 521 break; 522 #endif 523 } 524 525 return (0); 526 } 527