1 /* $OpenBSD: wsdisplay_compat_usl.c,v 1.31 2016/04/24 17:30:31 matthieu 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 39 #include <dev/wscons/wsconsio.h> 40 #include <dev/wscons/wsdisplayvar.h> 41 #include <dev/wscons/wscons_callbacks.h> 42 #include <dev/wscons/wsdisplay_usl_io.h> 43 44 #ifdef WSDISPLAY_DEBUG 45 #define DPRINTF(x) if (wsdisplaydebug) printf x 46 int wsdisplaydebug = 0; 47 #else 48 #define DPRINTF(x) 49 #endif 50 51 struct usl_syncdata { 52 struct wsscreen *s_scr; 53 struct process *s_process; 54 pid_t s_pid; 55 int s_flags; 56 #define SF_DETACHPENDING 1 57 #define SF_ATTACHPENDING 2 58 int s_acqsig, s_relsig; 59 int s_frsig; /* unused */ 60 void (*s_callback)(void *, int, int); 61 void *s_cbarg; 62 struct timeout s_attach_ch; 63 struct timeout s_detach_ch; 64 }; 65 66 int usl_sync_init(struct wsscreen *, struct usl_syncdata **, 67 struct process *, int, int, int); 68 void usl_sync_done(struct usl_syncdata *); 69 int usl_sync_check(struct usl_syncdata *); 70 struct usl_syncdata *usl_sync_get(struct wsscreen *); 71 72 int usl_detachproc(void *, int, void (*)(void *, int, int), void *); 73 int usl_detachack(struct usl_syncdata *, int); 74 void usl_detachtimeout(void *); 75 int usl_attachproc(void *, int, void (*)(void *, int, int), void *); 76 int usl_attachack(struct usl_syncdata *, int); 77 void usl_attachtimeout(void *); 78 79 static const struct wscons_syncops usl_syncops = { 80 usl_detachproc, 81 usl_attachproc, 82 #define _usl_sync_check ((int (*)(void *))usl_sync_check) 83 _usl_sync_check, 84 #define _usl_sync_destroy ((void (*)(void *))usl_sync_done) 85 _usl_sync_destroy 86 }; 87 88 #ifndef WSCOMPAT_USL_SYNCTIMEOUT 89 #define WSCOMPAT_USL_SYNCTIMEOUT 5 /* seconds */ 90 #endif 91 static int wscompat_usl_synctimeout = WSCOMPAT_USL_SYNCTIMEOUT; 92 93 int 94 usl_sync_init(struct wsscreen *scr, struct usl_syncdata **sdp, 95 struct process *pr, int acqsig, int relsig, int frsig) 96 { 97 struct usl_syncdata *sd; 98 int res; 99 100 if (acqsig <= 0 || acqsig >= NSIG || relsig <= 0 || relsig >= NSIG || 101 frsig <= 0 || frsig >= NSIG) 102 return (EINVAL); 103 sd = malloc(sizeof(*sd), M_DEVBUF, M_NOWAIT); 104 if (!sd) 105 return (ENOMEM); 106 sd->s_scr = scr; 107 sd->s_process = pr; 108 sd->s_pid = pr->ps_pid; 109 sd->s_flags = 0; 110 sd->s_acqsig = acqsig; 111 sd->s_relsig = relsig; 112 sd->s_frsig = frsig; 113 timeout_set(&sd->s_attach_ch, usl_attachtimeout, sd); 114 timeout_set(&sd->s_detach_ch, usl_detachtimeout, sd); 115 res = wsscreen_attach_sync(scr, &usl_syncops, sd); 116 if (res) { 117 free(sd, M_DEVBUF, sizeof(*sd)); 118 return (res); 119 } 120 *sdp = sd; 121 return (0); 122 } 123 124 void 125 usl_sync_done(struct usl_syncdata *sd) 126 { 127 if (sd->s_flags & SF_DETACHPENDING) { 128 timeout_del(&sd->s_detach_ch); 129 (*sd->s_callback)(sd->s_cbarg, 0, 0); 130 } 131 if (sd->s_flags & SF_ATTACHPENDING) { 132 timeout_del(&sd->s_attach_ch); 133 (*sd->s_callback)(sd->s_cbarg, ENXIO, 0); 134 } 135 wsscreen_detach_sync(sd->s_scr); 136 free(sd, M_DEVBUF, sizeof(*sd)); 137 } 138 139 int 140 usl_sync_check(struct usl_syncdata *sd) 141 { 142 if (sd->s_process == prfind(sd->s_pid)) 143 return (1); 144 DPRINTF(("usl_sync_check: process %d died\n", sd->s_pid)); 145 usl_sync_done(sd); 146 return (0); 147 } 148 149 struct usl_syncdata * 150 usl_sync_get(struct wsscreen *scr) 151 { 152 struct usl_syncdata *sd; 153 154 if (wsscreen_lookup_sync(scr, &usl_syncops, (void **)&sd)) 155 return (0); 156 return (sd); 157 } 158 159 int 160 usl_detachproc(void *cookie, int waitok, void (*callback)(void *, int, int), 161 void *cbarg) 162 { 163 struct usl_syncdata *sd = cookie; 164 165 if (!usl_sync_check(sd)) 166 return (0); 167 168 /* we really need a callback */ 169 if (!callback) 170 return (EINVAL); 171 172 /* 173 * Normally, this is called from the controlling process. 174 * It is supposed to reply with a VT_RELDISP ioctl(), so 175 * it is not useful to tsleep() here. 176 */ 177 sd->s_callback = callback; 178 sd->s_cbarg = cbarg; 179 sd->s_flags |= SF_DETACHPENDING; 180 prsignal(sd->s_process, sd->s_relsig); 181 timeout_add_sec(&sd->s_detach_ch, wscompat_usl_synctimeout); 182 183 return (EAGAIN); 184 } 185 186 int 187 usl_detachack(struct usl_syncdata *sd, int ack) 188 { 189 if (!(sd->s_flags & SF_DETACHPENDING)) { 190 DPRINTF(("usl_detachack: not detaching\n")); 191 return (EINVAL); 192 } 193 194 timeout_del(&sd->s_detach_ch); 195 sd->s_flags &= ~SF_DETACHPENDING; 196 197 if (sd->s_callback) 198 (*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1); 199 200 return (0); 201 } 202 203 void 204 usl_detachtimeout(void *arg) 205 { 206 struct usl_syncdata *sd = arg; 207 208 DPRINTF(("usl_detachtimeout\n")); 209 210 if (!(sd->s_flags & SF_DETACHPENDING)) { 211 DPRINTF(("usl_detachtimeout: not detaching\n")); 212 return; 213 } 214 215 sd->s_flags &= ~SF_DETACHPENDING; 216 217 if (sd->s_callback) 218 (*sd->s_callback)(sd->s_cbarg, EIO, 0); 219 220 (void) usl_sync_check(sd); 221 } 222 223 int 224 usl_attachproc(void *cookie, int waitok, void (*callback)(void *, int, int), 225 void *cbarg) 226 { 227 struct usl_syncdata *sd = cookie; 228 229 if (!usl_sync_check(sd)) 230 return (0); 231 232 /* we really need a callback */ 233 if (!callback) 234 return (EINVAL); 235 236 sd->s_callback = callback; 237 sd->s_cbarg = cbarg; 238 sd->s_flags |= SF_ATTACHPENDING; 239 prsignal(sd->s_process, sd->s_acqsig); 240 timeout_add_sec(&sd->s_attach_ch, wscompat_usl_synctimeout); 241 242 return (EAGAIN); 243 } 244 245 int 246 usl_attachack(struct usl_syncdata *sd, int ack) 247 { 248 if (!(sd->s_flags & SF_ATTACHPENDING)) { 249 DPRINTF(("usl_attachack: not attaching\n")); 250 return (EINVAL); 251 } 252 253 timeout_del(&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 void 263 usl_attachtimeout(void *arg) 264 { 265 struct usl_syncdata *sd = arg; 266 267 DPRINTF(("usl_attachtimeout\n")); 268 269 if (!(sd->s_flags & SF_ATTACHPENDING)) { 270 DPRINTF(("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 ss 322 return (0); 323 324 default: 325 return (-1); 326 } 327 328 return (0); 329 } 330 331 int 332 wsdisplay_usl_ioctl2(struct wsdisplay_softc *sc, struct wsscreen *scr, 333 u_long cmd, caddr_t data, int flag, struct proc *p) 334 { 335 int intarg, res; 336 u_long req; 337 void *arg; 338 struct usl_syncdata *sd; 339 struct wskbd_bell_data bd; 340 341 switch (cmd) { 342 case VT_SETMODE: 343 #define newmode ((struct vt_mode *)data) 344 if (newmode->mode == VT_PROCESS) { 345 res = usl_sync_init(scr, &sd, p->p_p, newmode->acqsig, 346 newmode->relsig, newmode->frsig); 347 if (res) 348 return (res); 349 } else { 350 sd = usl_sync_get(scr); 351 if (sd) 352 usl_sync_done(sd); 353 } 354 #undef newmode 355 return (0); 356 case VT_GETMODE: 357 #define cmode ((struct vt_mode *)data) 358 sd = usl_sync_get(scr); 359 if (sd) { 360 cmode->mode = VT_PROCESS; 361 cmode->relsig = sd->s_relsig; 362 cmode->acqsig = sd->s_acqsig; 363 cmode->frsig = sd->s_frsig; 364 } else 365 cmode->mode = VT_AUTO; 366 #undef cmode 367 return (0); 368 case VT_RELDISP: 369 #define d (*(int *)data) 370 sd = usl_sync_get(scr); 371 if (!sd) 372 return (EINVAL); 373 switch (d) { 374 case VT_FALSE: 375 case VT_TRUE: 376 return (usl_detachack(sd, (d == VT_TRUE))); 377 case VT_ACKACQ: 378 return (usl_attachack(sd, 1)); 379 default: 380 return (EINVAL); 381 } 382 #undef d 383 return (0); 384 385 case KDENABIO: 386 case KDDISABIO: 387 /* 388 * This is a lie, but non-x86 platforms are not supposed to 389 * issue these ioctls anyway. 390 */ 391 return (0); 392 393 case KDSETRAD: 394 /* XXX ignore for now */ 395 return (0); 396 397 default: 398 return (-1); 399 400 /* 401 * the following are converted to wsdisplay ioctls 402 */ 403 case KDSETMODE: 404 req = WSDISPLAYIO_SMODE; 405 #define d (*(int *)data) 406 switch (d) { 407 case KD_GRAPHICS: 408 intarg = WSDISPLAYIO_MODE_MAPPED; 409 break; 410 case KD_TEXT: 411 intarg = WSDISPLAYIO_MODE_EMUL; 412 break; 413 default: 414 return (EINVAL); 415 } 416 #undef d 417 arg = &intarg; 418 break; 419 case KDMKTONE: 420 req = WSKBDIO_COMPLEXBELL; 421 #define d (*(int *)data) 422 if (d) { 423 #define PCVT_SYSBEEPF 1193182 424 if (d >> 16) { 425 bd.which = WSKBD_BELL_DOPERIOD; 426 bd.period = d >> 16; /* ms */ 427 } 428 else 429 bd.which = 0; 430 if (d & 0xffff) { 431 bd.which |= WSKBD_BELL_DOPITCH; 432 bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */ 433 } 434 } else 435 bd.which = 0; /* default */ 436 #undef d 437 arg = &bd; 438 break; 439 case KDSETLED: 440 req = WSKBDIO_SETLEDS; 441 intarg = 0; 442 #define d (*(int *)data) 443 if (d & LED_CAP) 444 intarg |= WSKBD_LED_CAPS; 445 if (d & LED_NUM) 446 intarg |= WSKBD_LED_NUM; 447 if (d & LED_SCR) 448 intarg |= WSKBD_LED_SCROLL; 449 #undef d 450 arg = &intarg; 451 break; 452 case KDGETLED: 453 req = WSKBDIO_GETLEDS; 454 arg = &intarg; 455 break; 456 #ifdef WSDISPLAY_COMPAT_RAWKBD 457 case KDSKBMODE: 458 req = WSKBDIO_SETMODE; 459 switch (*(int *)data) { 460 case K_RAW: 461 intarg = WSKBD_RAW; 462 break; 463 case K_XLATE: 464 intarg = WSKBD_TRANSLATED; 465 break; 466 default: 467 return (EINVAL); 468 } 469 arg = &intarg; 470 break; 471 case KDGKBMODE: 472 req = WSKBDIO_GETMODE; 473 arg = &intarg; 474 break; 475 #endif 476 } 477 478 res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, p); 479 if (res) 480 return (res); 481 482 switch (cmd) { 483 case KDGETLED: 484 #define d (*(int *)data) 485 d = 0; 486 if (intarg & WSKBD_LED_CAPS) 487 d |= LED_CAP; 488 if (intarg & WSKBD_LED_NUM) 489 d |= LED_NUM; 490 if (intarg & WSKBD_LED_SCROLL) 491 d |= LED_SCR; 492 #undef d 493 break; 494 #ifdef WSDISPLAY_COMPAT_RAWKBD 495 case KDGKBMODE: 496 *(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE); 497 break; 498 #endif 499 } 500 501 return (0); 502 } 503