1/* $OpenBSD: wsdisplay_compat_usl.c,v 1.34 2024/04/13 23:44:11 jsg 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 47int wsdisplaydebug = 0; 48#else 49#define DPRINTF(x) 50#endif 51 52struct 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 67int usl_sync_init(struct wsscreen *, struct usl_syncdata **, 68 struct process *, int, int, int); 69void usl_sync_done(struct usl_syncdata *); 70int usl_sync_check(struct usl_syncdata *); 71struct usl_syncdata *usl_sync_get(struct wsscreen *); 72 73int usl_detachproc(void *, int, void (*)(void *, int, int), void *); 74int usl_detachack(struct usl_syncdata *, int); 75void usl_detachtimeout(void *); 76int usl_attachproc(void *, int, void (*)(void *, int, int), void *); 77int usl_attachack(struct usl_syncdata *, int); 78void usl_attachtimeout(void *); 79 80static 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 92static int wscompat_usl_synctimeout = WSCOMPAT_USL_SYNCTIMEOUT; 93 94int 95usl_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 125void 126usl_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 140int 141usl_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 150struct usl_syncdata * 151usl_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 160int 161usl_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 187int 188usl_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 204void 205usl_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 224int 225usl_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 246int 247usl_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 263void 264usl_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 283int 284wsdisplay_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 = *(long *)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 = *(long *)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 336int 337wsdisplay_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)(*(long *)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)(*(long *)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)(*(long *)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 } else 445 bd.which = 0; 446 if (d & 0xffff) { 447 bd.which |= WSKBD_BELL_DOPITCH; 448 bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */ 449 } 450 } else 451 bd.which = 0; /* default */ 452#undef d 453 arg = &bd; 454 break; 455 case KDSETLED: 456 if ((flag & FWRITE) == 0) 457 return (EACCES); 458 req = WSKBDIO_SETLEDS; 459 intarg = 0; 460#define d ((int)(*(long *)data)) 461 if (d & LED_CAP) 462 intarg |= WSKBD_LED_CAPS; 463 if (d & LED_NUM) 464 intarg |= WSKBD_LED_NUM; 465 if (d & LED_SCR) 466 intarg |= WSKBD_LED_SCROLL; 467#undef d 468 arg = &intarg; 469 break; 470 case KDGETLED: 471 req = WSKBDIO_GETLEDS; 472 arg = &intarg; 473 break; 474#ifdef WSDISPLAY_COMPAT_RAWKBD 475 case KDSKBMODE: 476 if ((flag & FWRITE) == 0) 477 return (EACCES); 478 req = WSKBDIO_SETMODE; 479 switch ((int)(*(long *)data)) { 480 case K_RAW: 481 intarg = WSKBD_RAW; 482 break; 483 case K_XLATE: 484 intarg = WSKBD_TRANSLATED; 485 break; 486 default: 487 return (EINVAL); 488 } 489 arg = &intarg; 490 break; 491 case KDGKBMODE: 492 req = WSKBDIO_GETMODE; 493 arg = &intarg; 494 break; 495#endif 496 } 497 498 res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, p); 499 if (res) 500 return (res); 501 502 switch (cmd) { 503 case KDGETLED: 504#define d (*(int *)data) 505 d = 0; 506 if (intarg & WSKBD_LED_CAPS) 507 d |= LED_CAP; 508 if (intarg & WSKBD_LED_NUM) 509 d |= LED_NUM; 510 if (intarg & WSKBD_LED_SCROLL) 511 d |= LED_SCR; 512#undef d 513 break; 514#ifdef WSDISPLAY_COMPAT_RAWKBD 515 case KDGKBMODE: 516 *(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE); 517 break; 518#endif 519 } 520 521 return (0); 522} 523