1 /* $OpenBSD: wsdisplay_compat_usl.c,v 1.32 2017/01/23 04:43:46 deraadt 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/kernel.h> 34 #include <sys/proc.h> 35 #include <sys/signalvar.h> 36 #include <sys/malloc.h> 37 #include <sys/errno.h> 38 #include <sys/fcntl.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 process *s_process; 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 process *, 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, 96 struct process *pr, 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(*sd), M_DEVBUF, M_NOWAIT); 105 if (!sd) 106 return (ENOMEM); 107 sd->s_scr = scr; 108 sd->s_process = pr; 109 sd->s_pid = pr->ps_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, sizeof(*sd)); 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, sizeof(*sd)); 138 } 139 140 int 141 usl_sync_check(struct usl_syncdata *sd) 142 { 143 if (sd->s_process == prfind(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 prsignal(sd->s_process, 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 prsignal(sd->s_process, 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 if ((flag & FWRITE) == 0) 305 return (EACCES); 306 idx = *(int *)data - 1; 307 if (idx < 0) 308 return (EINVAL); 309 return (wsdisplay_switch((struct device *)sc, idx, 1)); 310 case VT_WAITACTIVE: 311 if ((flag & FWRITE) == 0) 312 return (EACCES); 313 idx = *(int *)data - 1; 314 if (idx < 0) 315 return (EINVAL); 316 return (wsscreen_switchwait(sc, idx)); 317 case VT_GETSTATE: 318 #define ss ((struct vt_stat *)data) 319 idx = wsdisplay_getactivescreen(sc); 320 ss->v_active = idx + 1; 321 ss->v_state = 0; 322 maxidx = wsdisplay_maxscreenidx(sc); 323 for (idx = 0; idx <= maxidx; idx++) 324 if (wsdisplay_screenstate(sc, idx) == EBUSY) 325 ss->v_state |= (1 << (idx + 1)); 326 #undef ss 327 return (0); 328 329 default: 330 return (-1); 331 } 332 333 return (0); 334 } 335 336 int 337 wsdisplay_usl_ioctl2(struct wsdisplay_softc *sc, struct wsscreen *scr, 338 u_long cmd, caddr_t data, int flag, struct proc *p) 339 { 340 int intarg, res; 341 u_long req; 342 void *arg; 343 struct usl_syncdata *sd; 344 struct wskbd_bell_data bd; 345 346 switch (cmd) { 347 case VT_SETMODE: 348 if ((flag & FWRITE) == 0) 349 return (EACCES); 350 #define newmode ((struct vt_mode *)data) 351 if (newmode->mode == VT_PROCESS) { 352 res = usl_sync_init(scr, &sd, p->p_p, newmode->acqsig, 353 newmode->relsig, newmode->frsig); 354 if (res) 355 return (res); 356 } else { 357 sd = usl_sync_get(scr); 358 if (sd) 359 usl_sync_done(sd); 360 } 361 #undef newmode 362 return (0); 363 case VT_GETMODE: 364 #define cmode ((struct vt_mode *)data) 365 sd = usl_sync_get(scr); 366 if (sd) { 367 cmode->mode = VT_PROCESS; 368 cmode->relsig = sd->s_relsig; 369 cmode->acqsig = sd->s_acqsig; 370 cmode->frsig = sd->s_frsig; 371 } else 372 cmode->mode = VT_AUTO; 373 #undef cmode 374 return (0); 375 case VT_RELDISP: 376 if ((flag & FWRITE) == 0) 377 return (EACCES); 378 #define d (*(int *)data) 379 sd = usl_sync_get(scr); 380 if (!sd) 381 return (EINVAL); 382 switch (d) { 383 case VT_FALSE: 384 case VT_TRUE: 385 return (usl_detachack(sd, (d == VT_TRUE))); 386 case VT_ACKACQ: 387 return (usl_attachack(sd, 1)); 388 default: 389 return (EINVAL); 390 } 391 #undef d 392 return (0); 393 394 case KDENABIO: 395 case KDDISABIO: 396 if ((flag & FWRITE) == 0) 397 return (EACCES); 398 /* 399 * This is a lie, but non-x86 platforms are not supposed to 400 * issue these ioctls anyway. 401 */ 402 return (0); 403 404 case KDSETRAD: 405 if ((flag & FWRITE) == 0) 406 return (EACCES); 407 /* XXX ignore for now */ 408 return (0); 409 410 default: 411 return (-1); 412 413 /* 414 * the following are converted to wsdisplay ioctls 415 */ 416 case KDSETMODE: 417 if ((flag & FWRITE) == 0) 418 return (EACCES); 419 req = WSDISPLAYIO_SMODE; 420 #define d (*(int *)data) 421 switch (d) { 422 case KD_GRAPHICS: 423 intarg = WSDISPLAYIO_MODE_MAPPED; 424 break; 425 case KD_TEXT: 426 intarg = WSDISPLAYIO_MODE_EMUL; 427 break; 428 default: 429 return (EINVAL); 430 } 431 #undef d 432 arg = &intarg; 433 break; 434 case KDMKTONE: 435 if ((flag & FWRITE) == 0) 436 return (EACCES); 437 req = WSKBDIO_COMPLEXBELL; 438 #define d (*(int *)data) 439 if (d) { 440 #define PCVT_SYSBEEPF 1193182 441 if (d >> 16) { 442 bd.which = WSKBD_BELL_DOPERIOD; 443 bd.period = d >> 16; /* ms */ 444 } 445 else 446 bd.which = 0; 447 if (d & 0xffff) { 448 bd.which |= WSKBD_BELL_DOPITCH; 449 bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */ 450 } 451 } else 452 bd.which = 0; /* default */ 453 #undef d 454 arg = &bd; 455 break; 456 case KDSETLED: 457 if ((flag & FWRITE) == 0) 458 return (EACCES); 459 req = WSKBDIO_SETLEDS; 460 intarg = 0; 461 #define d (*(int *)data) 462 if (d & LED_CAP) 463 intarg |= WSKBD_LED_CAPS; 464 if (d & LED_NUM) 465 intarg |= WSKBD_LED_NUM; 466 if (d & LED_SCR) 467 intarg |= WSKBD_LED_SCROLL; 468 #undef d 469 arg = &intarg; 470 break; 471 case KDGETLED: 472 req = WSKBDIO_GETLEDS; 473 arg = &intarg; 474 break; 475 #ifdef WSDISPLAY_COMPAT_RAWKBD 476 case KDSKBMODE: 477 if ((flag & FWRITE) == 0) 478 return (EACCES); 479 req = WSKBDIO_SETMODE; 480 switch (*(int *)data) { 481 case K_RAW: 482 intarg = WSKBD_RAW; 483 break; 484 case K_XLATE: 485 intarg = WSKBD_TRANSLATED; 486 break; 487 default: 488 return (EINVAL); 489 } 490 arg = &intarg; 491 break; 492 case KDGKBMODE: 493 req = WSKBDIO_GETMODE; 494 arg = &intarg; 495 break; 496 #endif 497 } 498 499 res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, p); 500 if (res) 501 return (res); 502 503 switch (cmd) { 504 case KDGETLED: 505 #define d (*(int *)data) 506 d = 0; 507 if (intarg & WSKBD_LED_CAPS) 508 d |= LED_CAP; 509 if (intarg & WSKBD_LED_NUM) 510 d |= LED_NUM; 511 if (intarg & WSKBD_LED_SCROLL) 512 d |= LED_SCR; 513 #undef d 514 break; 515 #ifdef WSDISPLAY_COMPAT_RAWKBD 516 case KDGKBMODE: 517 *(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE); 518 break; 519 #endif 520 } 521 522 return (0); 523 } 524