1/* $NetBSD: wsdisplay_compat_usl.c,v 1.46 2009/10/04 22:24:15 christos 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.46 2009/10/04 22:24:15 christos 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#include <sys/kauth.h> 45 46#include <dev/wscons/wsconsio.h> 47#include <dev/wscons/wsdisplayvar.h> 48#include <dev/wscons/wscons_callbacks.h> 49#include <dev/wscons/wsdisplay_usl_io.h> 50 51#include "opt_wsdisplay_compat.h" 52 53struct usl_syncdata { 54 struct wsscreen *s_scr; 55 struct proc *s_proc; 56 pid_t s_pid; 57 int s_flags; 58#define SF_DETACHPENDING 1 59#define SF_ATTACHPENDING 2 60 int s_acqsig, s_relsig; 61 int s_frsig; /* unused */ 62 void (*s_callback)(void *, int, int); 63 void *s_cbarg; 64 callout_t s_attach_ch; 65 callout_t s_detach_ch; 66}; 67 68static int usl_sync_init(struct wsscreen *, struct usl_syncdata **, 69 struct proc *, int, int, int); 70static void usl_sync_done(struct usl_syncdata *); 71static int usl_sync_check(void *); 72static int usl_sync_check_sig(struct usl_syncdata *, int, int); 73static struct usl_syncdata *usl_sync_get(struct wsscreen *); 74 75static int usl_detachproc(void *, int, void (*)(void *, int, int), void *); 76static int usl_detachack(struct usl_syncdata *, int); 77static void usl_detachtimeout(void *); 78static int usl_attachproc(void *, int, void (*)(void *, int, int), void *); 79static int usl_attachack(struct usl_syncdata *, int); 80static void usl_attachtimeout(void *); 81 82static const struct wscons_syncops usl_syncops = { 83 usl_detachproc, 84 usl_attachproc, 85 usl_sync_check, 86#define _usl_sync_destroy ((void (*)(void *))usl_sync_done) 87 _usl_sync_destroy 88}; 89 90#ifndef WSCOMPAT_USL_SYNCTIMEOUT 91#define WSCOMPAT_USL_SYNCTIMEOUT 5 /* seconds */ 92#endif 93static int wscompat_usl_synctimeout = WSCOMPAT_USL_SYNCTIMEOUT; 94 95static int 96usl_sync_init(struct wsscreen *scr, struct usl_syncdata **sdp, 97 struct proc *p, int acqsig, int relsig, int frsig) 98{ 99 struct usl_syncdata *sd; 100 int res; 101 102 sd = malloc(sizeof(struct usl_syncdata), M_DEVBUF, M_WAITOK); 103 if (!sd) 104 return (ENOMEM); 105 sd->s_scr = scr; 106 sd->s_proc = p; 107 sd->s_pid = p->p_pid; 108 sd->s_flags = 0; 109 sd->s_acqsig = acqsig; 110 sd->s_relsig = relsig; 111 sd->s_frsig = frsig; 112 callout_init(&sd->s_attach_ch, 0); 113 callout_setfunc(&sd->s_attach_ch, usl_attachtimeout, sd); 114 callout_init(&sd->s_detach_ch, 0); 115 callout_setfunc(&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 125static void 126usl_sync_done(struct usl_syncdata *sd) 127{ 128 if (sd->s_flags & SF_DETACHPENDING) { 129 callout_stop(&sd->s_detach_ch); 130 (*sd->s_callback)(sd->s_cbarg, 0, 0); 131 } 132 if (sd->s_flags & SF_ATTACHPENDING) { 133 callout_stop(&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 140static int 141usl_sync_check_sig(struct usl_syncdata *sd, int sig, int flags) 142{ 143 144 mutex_enter(proc_lock); 145 if (sd->s_proc == proc_find(sd->s_pid)) { 146 sd->s_flags |= flags; 147 if (sig) 148 psignal(sd->s_proc, sig); 149 mutex_exit(proc_lock); 150 return (1); 151 } 152 mutex_exit(proc_lock); 153 154 printf("usl_sync_check: process %d died\n", sd->s_pid); 155 usl_sync_done(sd); 156 return (0); 157} 158 159static int 160usl_sync_check(void *vsd) 161{ 162 163 struct usl_syncdata *sd = vsd; 164 return usl_sync_check_sig(sd, 0, 0); 165} 166 167static struct usl_syncdata * 168usl_sync_get(struct wsscreen *scr) 169{ 170 void *sd; 171 172 if (wsscreen_lookup_sync(scr, &usl_syncops, &sd)) 173 return (0); 174 return (struct usl_syncdata *)sd; 175} 176 177static int 178usl_detachproc(void *cookie, int waitok, 179 void (*callback)(void *, int, int), void *cbarg) 180{ 181 struct usl_syncdata *sd = cookie; 182 183 /* we really need a callback */ 184 if (!callback) 185 return (EINVAL); 186 187 /* 188 * Normally, this is called from the controlling process. 189 * Is is supposed to reply with a VT_RELDISP ioctl(), so 190 * it is not useful to tsleep() here. 191 */ 192 sd->s_callback = callback; 193 sd->s_cbarg = cbarg; 194 if (waitok) { 195 if (!usl_sync_check_sig(sd, sd->s_relsig, SF_DETACHPENDING)) 196 return (0); 197 } 198 199 callout_schedule(&sd->s_detach_ch, wscompat_usl_synctimeout * hz); 200 return (EAGAIN); 201} 202 203static int 204usl_detachack(struct usl_syncdata *sd, int ack) 205{ 206 if (!(sd->s_flags & SF_DETACHPENDING)) { 207 printf("usl_detachack: not detaching\n"); 208 return (EINVAL); 209 } 210 211 callout_stop(&sd->s_detach_ch); 212 sd->s_flags &= ~SF_DETACHPENDING; 213 214 if (sd->s_callback) 215 (*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1); 216 217 return (0); 218} 219 220static void 221usl_detachtimeout(void *arg) 222{ 223 struct usl_syncdata *sd = arg; 224 225 printf("usl_detachtimeout\n"); 226 227 if (!(sd->s_flags & SF_DETACHPENDING)) { 228 printf("usl_detachtimeout: not detaching\n"); 229 return; 230 } 231 232 sd->s_flags &= ~SF_DETACHPENDING; 233 234 if (sd->s_callback) 235 (*sd->s_callback)(sd->s_cbarg, EIO, 0); 236 237 (void) usl_sync_check(sd); 238} 239 240static int 241usl_attachproc(void *cookie, int waitok, 242 void (*callback)(void *, int, int), void *cbarg) 243{ 244 struct usl_syncdata *sd = cookie; 245 246 /* we really need a callback */ 247 if (!callback) 248 return (EINVAL); 249 250 sd->s_callback = callback; 251 sd->s_cbarg = cbarg; 252 if (!usl_sync_check_sig(sd, sd->s_acqsig, SF_ATTACHPENDING)) 253 return (0); 254 255 callout_schedule(&sd->s_attach_ch, wscompat_usl_synctimeout * hz); 256 return (EAGAIN); 257} 258 259static int 260usl_attachack(struct usl_syncdata *sd, int ack) 261{ 262 if (!(sd->s_flags & SF_ATTACHPENDING)) { 263 printf("usl_attachack: not attaching\n"); 264 return (EINVAL); 265 } 266 267 callout_stop(&sd->s_attach_ch); 268 sd->s_flags &= ~SF_ATTACHPENDING; 269 270 if (sd->s_callback) 271 (*sd->s_callback)(sd->s_cbarg, (ack ? 0 : EIO), 1); 272 273 return (0); 274} 275 276static void 277usl_attachtimeout(void *arg) 278{ 279 struct usl_syncdata *sd = arg; 280 281 printf("usl_attachtimeout\n"); 282 283 if (!(sd->s_flags & SF_ATTACHPENDING)) { 284 printf("usl_attachtimeout: not attaching\n"); 285 return; 286 } 287 288 sd->s_flags &= ~SF_ATTACHPENDING; 289 290 if (sd->s_callback) 291 (*sd->s_callback)(sd->s_cbarg, EIO, 0); 292 293 (void) usl_sync_check(sd); 294} 295 296int 297wsdisplay_usl_ioctl1(device_t dv, u_long cmd, void *data, 298 int flag, struct lwp *l) 299{ 300 struct wsdisplay_softc *sc = device_private(dv); 301 int idx, maxidx; 302 303 switch (cmd) { 304 case VT_OPENQRY: 305 maxidx = wsdisplay_maxscreenidx(sc); 306 for (idx = 0; idx <= maxidx; idx++) { 307 if (wsdisplay_screenstate(sc, idx) == 0) { 308 *(int *)data = idx + 1; 309 return (0); 310 } 311 } 312 return (ENXIO); 313 case VT_GETACTIVE: 314 idx = wsdisplay_getactivescreen(sc); 315 *(int *)data = idx + 1; 316 return (0); 317 case VT_ACTIVATE: 318 /* 319 * a gross and disgusting hack to make this abused up ioctl, 320 * which is a gross and disgusting hack on its own, work on 321 * LP64/BE - we want the lower 32bit so we simply dereference 322 * the argument pointer as long. May cause problems with 32bit 323 * kernels on sparc64? 324 */ 325 326 idx = *(long *)data - 1; 327 if (idx < 0) 328 return (EINVAL); 329 return (wsdisplay_switch(dv, idx, 1)); 330 case VT_WAITACTIVE: 331 idx = *(long *)data - 1; 332 if (idx < 0) 333 return (EINVAL); 334 return (wsscreen_switchwait(sc, idx)); 335 case VT_GETSTATE: 336#define ss ((struct vt_stat *)data) 337 idx = wsdisplay_getactivescreen(sc); 338 ss->v_active = idx + 1; 339 ss->v_state = 0; 340 maxidx = wsdisplay_maxscreenidx(sc); 341 for (idx = 0; idx <= maxidx; idx++) 342 if (wsdisplay_screenstate(sc, idx) == EBUSY) 343 ss->v_state |= (1 << (idx + 1)); 344#undef ss 345 return (0); 346 347#ifdef WSDISPLAY_COMPAT_PCVT 348 case VGAPCVTID: 349#define id ((struct pcvtid *)data) 350 strlcpy(id->name, "pcvt", sizeof(id->name)); 351 id->rmajor = 3; 352 id->rminor = 32; 353#undef id 354 return (0); 355#endif 356#ifdef WSDISPLAY_COMPAT_SYSCONS 357 case CONS_GETVERS: 358 *(int *)data = 0x200; /* version 2.0 */ 359 return (0); 360#endif 361 362 default: 363 return (EPASSTHROUGH); 364 } 365} 366 367int 368wsdisplay_usl_ioctl2(struct wsdisplay_softc *sc, struct wsscreen *scr, 369 u_long cmd, void *data, int flag, struct lwp *l) 370{ 371 struct proc *p = l->l_proc; 372 int intarg = 0, res; 373 u_long req; 374 void *arg; 375 struct usl_syncdata *sd; 376 struct wskbd_bell_data bd; 377 378 switch (cmd) { 379 case VT_SETMODE: 380#define newmode ((struct vt_mode *)data) 381 if (newmode->mode == VT_PROCESS) { 382 res = usl_sync_init(scr, &sd, p, newmode->acqsig, 383 newmode->relsig, newmode->frsig); 384 if (res) 385 return (res); 386 } else { 387 sd = usl_sync_get(scr); 388 if (sd) 389 usl_sync_done(sd); 390 } 391#undef newmode 392 return (0); 393 case VT_GETMODE: 394#define cmode ((struct vt_mode *)data) 395 sd = usl_sync_get(scr); 396 if (sd) { 397 cmode->mode = VT_PROCESS; 398 cmode->relsig = sd->s_relsig; 399 cmode->acqsig = sd->s_acqsig; 400 cmode->frsig = sd->s_frsig; 401 } else 402 cmode->mode = VT_AUTO; 403#undef cmode 404 return (0); 405 case VT_RELDISP: 406#define d (*(long *)data) 407 sd = usl_sync_get(scr); 408 if (!sd) 409 return (EINVAL); 410 switch (d) { 411 case VT_FALSE: 412 case VT_TRUE: 413 return (usl_detachack(sd, (d == VT_TRUE))); 414 case VT_ACKACQ: 415 return (usl_attachack(sd, 1)); 416 default: 417 return (EINVAL); 418 } 419#undef d 420 421 case KDENABIO: 422#if defined(__i386__) && (defined(COMPAT_11) || defined(COMPAT_FREEBSD)) 423 if (kauth_authorize_machdep(l->l_cred, KAUTH_MACHDEP_IOPL, 424 NULL, NULL, NULL, NULL) != 0) 425 return (EPERM); 426#endif 427 /* FALLTHRU */ 428 case KDDISABIO: 429#if defined(__i386__) && (defined(COMPAT_11) || defined(COMPAT_FREEBSD)) 430 { 431 /* XXX NJWLWP */ 432 struct trapframe *fp = (struct trapframe *)curlwp->l_md.md_regs; 433 if (cmd == KDENABIO) 434 fp->tf_eflags |= PSL_IOPL; 435 else 436 fp->tf_eflags &= ~PSL_IOPL; 437 } 438#endif 439 return (0); 440 case KDSETRAD: 441 /* XXX ignore for now */ 442 return (0); 443 444 default: 445 return (EPASSTHROUGH); 446 447 /* 448 * the following are converted to wsdisplay ioctls 449 */ 450 case KDSETMODE: 451 req = WSDISPLAYIO_SMODE; 452#define d (*(int *)data) 453 switch (d) { 454 case KD_GRAPHICS: 455 intarg = WSDISPLAYIO_MODE_MAPPED; 456 break; 457 case KD_TEXT: 458 intarg = WSDISPLAYIO_MODE_EMUL; 459 break; 460 default: 461 return (EINVAL); 462 } 463#undef d 464 arg = &intarg; 465 break; 466 case KDMKTONE: 467 req = WSKBDIO_COMPLEXBELL; 468#define d (*(int *)data) 469 if (d) { 470#define PCVT_SYSBEEPF 1193182 471 if (d >> 16) { 472 bd.which = WSKBD_BELL_DOPERIOD; 473 bd.period = d >> 16; /* ms */ 474 } 475 else 476 bd.which = 0; 477 if (d & 0xffff) { 478 bd.which |= WSKBD_BELL_DOPITCH; 479 bd.pitch = PCVT_SYSBEEPF/(d & 0xffff); /* Hz */ 480 } 481 } else 482 bd.which = 0; /* default */ 483#undef d 484 arg = &bd; 485 break; 486 case KDSETLED: 487 req = WSKBDIO_SETLEDS; 488 intarg = 0; 489#define d (*(int *)data) 490 if (d & LED_CAP) 491 intarg |= WSKBD_LED_CAPS; 492 if (d & LED_NUM) 493 intarg |= WSKBD_LED_NUM; 494 if (d & LED_SCR) 495 intarg |= WSKBD_LED_SCROLL; 496#undef d 497 arg = &intarg; 498 break; 499 case KDGETLED: 500 req = WSKBDIO_GETLEDS; 501 arg = &intarg; 502 break; 503#ifdef WSDISPLAY_COMPAT_RAWKBD 504 case KDSKBMODE: 505 req = WSKBDIO_SETMODE; 506 switch (*(int *)data) { 507 case K_RAW: 508 intarg = WSKBD_RAW; 509 break; 510 case K_XLATE: 511 intarg = WSKBD_TRANSLATED; 512 break; 513 default: 514 return (EINVAL); 515 } 516 arg = &intarg; 517 break; 518 case KDGKBMODE: 519 req = WSKBDIO_GETMODE; 520 arg = &intarg; 521 break; 522#endif 523 } 524 525 res = wsdisplay_internal_ioctl(sc, scr, req, arg, flag, l); 526 if (res != EPASSTHROUGH) 527 return (res); 528 529 switch (cmd) { 530 case KDGETLED: 531#define d (*(int *)data) 532 d = 0; 533 if (intarg & WSKBD_LED_CAPS) 534 d |= LED_CAP; 535 if (intarg & WSKBD_LED_NUM) 536 d |= LED_NUM; 537 if (intarg & WSKBD_LED_SCROLL) 538 d |= LED_SCR; 539#undef d 540 break; 541#ifdef WSDISPLAY_COMPAT_RAWKBD 542 case KDGKBMODE: 543 *(int *)data = (intarg == WSKBD_RAW ? K_RAW : K_XLATE); 544 break; 545#endif 546 } 547 548 return (0); 549} 550