1 /* $NetBSD: grf.c,v 1.21 2001/12/27 02:23:24 wiz Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1990, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * from: Utah $Hdr: grf.c 1.36 93/08/13$ 41 * 42 * @(#)grf.c 8.4 (Berkeley) 1/12/94 43 */ 44 45 /* 46 * Graphics display driver for the X68K machines. 47 * This is the hardware-independent portion of the driver. 48 * Hardware access is through the machine dependent grf switch routines. 49 */ 50 51 #include "opt_compat_hpux.h" 52 53 #include <sys/param.h> 54 #include <sys/systm.h> 55 #include <sys/device.h> 56 #include <sys/proc.h> 57 #include <sys/resourcevar.h> 58 #include <sys/ioctl.h> 59 #include <sys/file.h> 60 #include <sys/malloc.h> 61 #include <sys/vnode.h> 62 #include <sys/mman.h> 63 #include <sys/poll.h> 64 #include <sys/conf.h> 65 66 #include <machine/grfioctl.h> 67 68 #include <x68k/dev/grfvar.h> 69 #include <x68k/dev/itevar.h> 70 71 #include <machine/cpu.h> 72 73 #ifdef COMPAT_HPUX 74 #include <compat/hpux/hpux.h> 75 extern struct emul emul_hpux; 76 #endif 77 78 #include <uvm/uvm_extern.h> 79 #include <uvm/uvm_map.h> 80 81 #include <miscfs/specfs/specdev.h> 82 83 #include "ite.h" 84 #if NITE == 0 85 #define iteon(u,f) 86 #define iteoff(u,f) 87 #define ite_reinit(u) 88 #endif 89 90 #ifdef DEBUG 91 int grfdebug = 0; 92 #define GDB_DEVNO 0x01 93 #define GDB_MMAP 0x02 94 #define GDB_IOMAP 0x04 95 #define GDB_LOCK 0x08 96 #endif 97 98 cdev_decl(grf); 99 int grfon __P((dev_t)); 100 int grfoff __P((dev_t)); 101 off_t grfaddr __P((struct grf_softc *, off_t)); 102 int grfmap __P((dev_t, caddr_t *, struct proc *)); 103 int grfunmap __P((dev_t, caddr_t, struct proc *)); 104 105 extern struct cfdriver grf_cd; 106 107 /*ARGSUSED*/ 108 int 109 grfopen(dev, flags, mode, p) 110 dev_t dev; 111 int flags, mode; 112 struct proc *p; 113 { 114 int unit = GRFUNIT(dev); 115 register struct grf_softc *gp; 116 int error = 0; 117 118 if (unit >= grf_cd.cd_ndevs || 119 (gp = grf_cd.cd_devs[unit]) == NULL || 120 (gp->g_flags & GF_ALIVE) == 0) 121 return (ENXIO); 122 123 if ((gp->g_flags & (GF_OPEN|GF_EXCLUDE)) == (GF_OPEN|GF_EXCLUDE)) 124 return(EBUSY); 125 #ifdef COMPAT_HPUX 126 /* 127 * XXX: cannot handle both HPUX and BSD processes at the same time 128 */ 129 if (p->p_emul == &emul_hpux) 130 if (gp->g_flags & GF_BSDOPEN) 131 return(EBUSY); 132 else 133 gp->g_flags |= GF_HPUXOPEN; 134 else 135 if (gp->g_flags & GF_HPUXOPEN) 136 return(EBUSY); 137 else 138 gp->g_flags |= GF_BSDOPEN; 139 #endif 140 /* 141 * First open. 142 * XXX: always put in graphics mode. 143 */ 144 error = 0; 145 if ((gp->g_flags & GF_OPEN) == 0) { 146 gp->g_flags |= GF_OPEN; 147 error = grfon(dev); 148 } 149 return(error); 150 } 151 152 /*ARGSUSED*/ 153 int 154 grfclose(dev, flags, mode, p) 155 dev_t dev; 156 int flags, mode; 157 struct proc *p; 158 { 159 register struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)]; 160 161 if ((gp->g_flags & GF_ALIVE) == 0) 162 return (ENXIO); 163 164 (void) grfoff(dev); 165 #ifdef COMPAT_HPUX 166 (void) grfunlock(gp); 167 #endif 168 gp->g_flags &= GF_ALIVE; 169 return(0); 170 } 171 172 /*ARGSUSED*/ 173 int 174 grfioctl(dev, cmd, data, flag, p) 175 dev_t dev; 176 u_long cmd; 177 caddr_t data; 178 int flag; 179 struct proc *p; 180 { 181 int unit = GRFUNIT(dev); 182 register struct grf_softc *gp = grf_cd.cd_devs[unit]; 183 int error; 184 185 if ((gp->g_flags & GF_ALIVE) == 0) 186 return (ENXIO); 187 188 #ifdef COMPAT_HPUX 189 if (p->p_emul == &emul_hpux) 190 return(hpuxgrfioctl(dev, cmd, data, flag, p)); 191 #endif 192 error = 0; 193 switch (cmd) { 194 195 case GRFIOCGINFO: 196 memcpy(data, (caddr_t)&gp->g_display, sizeof(struct grfinfo)); 197 break; 198 199 case GRFIOCON: 200 error = grfon(dev); 201 break; 202 203 case GRFIOCOFF: 204 error = grfoff(dev); 205 break; 206 207 case GRFIOCMAP: 208 error = grfmap(dev, (caddr_t *)data, p); 209 break; 210 211 case GRFIOCUNMAP: 212 error = grfunmap(dev, *(caddr_t *)data, p); 213 break; 214 215 case GRFSETVMODE: 216 error = (*gp->g_sw->gd_mode)(gp, GM_GRFSETVMODE, data); 217 if (error == 0) 218 ite_reinit(unit); 219 break; 220 221 default: 222 error = EINVAL; 223 break; 224 225 } 226 return(error); 227 } 228 229 /*ARGSUSED*/ 230 int 231 grfpoll(dev, events, p) 232 dev_t dev; 233 int events; 234 struct proc *p; 235 { 236 237 return (events & (POLLOUT | POLLWRNORM)); 238 } 239 240 /*ARGSUSED*/ 241 paddr_t 242 grfmmap(dev, off, prot) 243 dev_t dev; 244 off_t off; 245 int prot; 246 { 247 248 return (grfaddr(grf_cd.cd_devs[GRFUNIT(dev)], off)); 249 } 250 251 int 252 grfon(dev) 253 dev_t dev; 254 { 255 int unit = GRFUNIT(dev); 256 struct grf_softc *gp = grf_cd.cd_devs[unit]; 257 258 /* 259 * XXX: iteoff call relies on devices being in same order 260 * as ITEs and the fact that iteoff only uses the minor part 261 * of the dev arg. 262 */ 263 iteoff(unit, 2); 264 return((*gp->g_sw->gd_mode)(gp, 265 (dev&GRFOVDEV) ? GM_GRFOVON : GM_GRFON, 266 (caddr_t)0)); 267 } 268 269 int 270 grfoff(dev) 271 dev_t dev; 272 { 273 int unit = GRFUNIT(dev); 274 struct grf_softc *gp = grf_cd.cd_devs[unit]; 275 int error; 276 277 (void) grfunmap(dev, (caddr_t)0, curproc); 278 error = (*gp->g_sw->gd_mode)(gp, 279 (dev&GRFOVDEV) ? GM_GRFOVOFF : GM_GRFOFF, 280 (caddr_t)0); 281 /* XXX: see comment for iteoff above */ 282 iteon(unit, 2); 283 return(error); 284 } 285 286 off_t 287 grfaddr(gp, off) 288 struct grf_softc *gp; 289 off_t off; 290 { 291 register struct grfinfo *gi = &gp->g_display; 292 293 /* control registers */ 294 if (off >= 0 && off < gi->gd_regsize) 295 return(((u_int)gi->gd_regaddr + off) >> PGSHIFT); 296 297 /* frame buffer */ 298 if (off >= gi->gd_regsize && off < gi->gd_regsize+gi->gd_fbsize) { 299 off -= gi->gd_regsize; 300 return(((u_int)gi->gd_fbaddr + off) >> PGSHIFT); 301 } 302 /* bogus */ 303 return(-1); 304 } 305 306 /* 307 * HP-UX compatibility routines 308 */ 309 #ifdef COMPAT_HPUX 310 311 /*ARGSUSED*/ 312 int 313 hpuxgrfioctl(dev, cmd, data, flag, p) 314 dev_t dev; 315 int cmd; 316 caddr_t data; 317 int flag; 318 struct proc *p; 319 { 320 register struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)]; 321 int error; 322 323 error = 0; 324 switch (cmd) { 325 326 case GCID: 327 *(int *)data = gp->g_display.gd_id; 328 break; 329 330 case GCON: 331 error = grfon(dev); 332 break; 333 334 case GCOFF: 335 error = grfoff(dev); 336 break; 337 338 case GCLOCK: 339 error = grflock(gp, 1); 340 break; 341 342 case GCUNLOCK: 343 error = grfunlock(gp); 344 break; 345 346 case GCAON: 347 case GCAOFF: 348 break; 349 350 /* GCSTATIC is implied by our implementation */ 351 case GCSTATIC_CMAP: 352 case GCVARIABLE_CMAP: 353 break; 354 355 /* map in control regs and frame buffer */ 356 case GCMAP: 357 error = grfmap(dev, (caddr_t *)data, p); 358 break; 359 360 case GCUNMAP: 361 error = grfunmap(dev, *(caddr_t *)data, p); 362 /* XXX: HP-UX uses GCUNMAP to get rid of GCSLOT memory */ 363 if (error) 364 error = grflckunmmap(dev, *(caddr_t *)data); 365 break; 366 367 case GCSLOT: 368 { 369 struct grf_slot *sp = (struct grf_slot *)data; 370 371 sp->slot = grffindpid(gp); 372 if (sp->slot) { 373 error = grflckmmap(dev, (caddr_t *)&sp->addr); 374 if (error && gp->g_pid) { 375 free((caddr_t)gp->g_pid, M_DEVBUF); 376 gp->g_pid = NULL; 377 } 378 } else 379 error = EINVAL; /* XXX */ 380 break; 381 } 382 383 case GCDESCRIBE: 384 error = (*gp->g_sw->gd_mode)(gp, GM_DESCRIBE, data); 385 break; 386 387 /* 388 * XXX: only used right now to map in rbox control registers 389 * Will be replaced in the future with a real IOMAP interface. 390 */ 391 case IOMAPMAP: 392 error = iommap(dev, (caddr_t *)data); 393 #if 0 394 /* 395 * It may not be worth kludging this (using p_devtmp) to 396 * make this work. It was an undocumented side-effect 397 * in HP-UX that the mapped address was the return value 398 * of the ioctl. The only thing I remember that counted 399 * on this behavior was the rbox X10 server. 400 */ 401 if (!error) 402 u.u_r.r_val1 = *(int *)data; /* XXX: this sux */ 403 #endif 404 break; 405 406 case IOMAPUNMAP: 407 error = iounmmap(dev, *(caddr_t *)data); 408 break; 409 410 default: 411 error = EINVAL; 412 break; 413 } 414 return(error); 415 } 416 417 int 418 grflock(gp, block) 419 register struct grf_softc *gp; 420 int block; 421 { 422 struct proc *p = curproc; /* XXX */ 423 int error; 424 extern char devioc[]; 425 426 #ifdef DEBUG 427 if (grfdebug & GDB_LOCK) 428 printf("grflock(%d): flags %x lockpid %x\n", 429 p->p_pid, gp->g_flags, 430 gp->g_lockp ? gp->g_lockp->p_pid : -1); 431 #endif 432 if (gp->g_pid) { 433 #ifdef DEBUG 434 if (grfdebug & GDB_LOCK) 435 printf(" lockpslot %d lockslot %d lock[lockslot] %d\n", 436 gp->g_lock->gl_lockslot, gp->g_lockpslot, 437 gp->g_lock->gl_locks[gp->g_lockpslot]); 438 #endif 439 gp->g_lock->gl_lockslot = 0; 440 if (gp->g_lock->gl_locks[gp->g_lockpslot] == 0) { 441 gp->g_lockp = NULL; 442 gp->g_lockpslot = 0; 443 } 444 } 445 if (gp->g_lockp) { 446 if (gp->g_lockp == p) 447 return(EBUSY); 448 if (!block) 449 return(OEAGAIN); 450 do { 451 gp->g_flags |= GF_WANTED; 452 if ((error = tsleep((caddr_t)&gp->g_flags, 453 (PZERO+1) | PCATCH, devioc, 0))) 454 return (error); 455 } while (gp->g_lockp); 456 } 457 gp->g_lockp = p; 458 if (gp->g_pid) { 459 int slot = grffindpid(gp); 460 461 #ifdef DEBUG 462 if (grfdebug & GDB_LOCK) 463 printf(" slot %d\n", slot); 464 #endif 465 gp->g_lockpslot = gp->g_lock->gl_lockslot = slot; 466 gp->g_lock->gl_locks[slot] = 1; 467 } 468 return(0); 469 } 470 471 int 472 grfunlock(gp) 473 register struct grf_softc *gp; 474 { 475 #ifdef DEBUG 476 if (grfdebug & GDB_LOCK) 477 printf("grfunlock(%d): flags %x lockpid %d\n", 478 curproc->p_pid, gp->g_flags, 479 gp->g_lockp ? gp->g_lockp->p_pid : -1); 480 #endif 481 if (gp->g_lockp != curproc) 482 return(EBUSY); 483 if (gp->g_pid) { 484 #ifdef DEBUG 485 if (grfdebug & GDB_LOCK) 486 printf(" lockpslot %d lockslot %d lock[lockslot] %d\n", 487 gp->g_lock->gl_lockslot, gp->g_lockpslot, 488 gp->g_lock->gl_locks[gp->g_lockpslot]); 489 #endif 490 gp->g_lock->gl_locks[gp->g_lockpslot] = 0; 491 gp->g_lockpslot = gp->g_lock->gl_lockslot = 0; 492 } 493 if (gp->g_flags & GF_WANTED) { 494 wakeup((caddr_t)&gp->g_flags); 495 gp->g_flags &= ~GF_WANTED; 496 } 497 gp->g_lockp = NULL; 498 return(0); 499 } 500 501 /* 502 * Convert a BSD style minor devno to HPUX style. 503 * We cannot just create HPUX style nodes as they require 24 bits 504 * of minor device number and we only have 8. 505 * XXX: This may give the wrong result for remote stats of other 506 * machines where device 10 exists. 507 */ 508 int 509 grfdevno(dev) 510 dev_t dev; 511 { 512 int unit = GRFUNIT(dev); 513 struct grf_softc *gp; 514 int newdev; 515 516 if (unit >= grf_cd.cd_ndevs || 517 (gp = grf_cd.cd_devs[unit]) == NULL || 518 (gp->g_flags&GF_ALIVE) == 0) 519 return(bsdtohpuxdev(dev)); 520 /* magic major number */ 521 newdev = 12 << 24; 522 /* now construct minor number */ 523 if (gp->g_display.gd_regaddr != (caddr_t)GRFIADDR) { 524 int sc = patosc(gp->g_display.gd_regaddr); 525 newdev |= (sc << 16) | 0x200; 526 } 527 if (dev & GRFIMDEV) 528 newdev |= 0x02; 529 else if (dev & GRFOVDEV) 530 newdev |= 0x01; 531 #ifdef DEBUG 532 if (grfdebug & GDB_DEVNO) 533 printf("grfdevno: dev %x newdev %x\n", dev, newdev); 534 #endif 535 return(newdev); 536 } 537 538 #endif /* COMPAT_HPUX */ 539 540 int 541 grfmap(dev, addrp, p) 542 dev_t dev; 543 caddr_t *addrp; 544 struct proc *p; 545 { 546 struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)]; 547 int len, error; 548 struct vnode vn; 549 struct specinfo si; 550 int flags; 551 552 #ifdef DEBUG 553 if (grfdebug & GDB_MMAP) 554 printf("grfmap(%d): addr %p\n", p->p_pid, *addrp); 555 #endif 556 len = gp->g_display.gd_regsize + gp->g_display.gd_fbsize; 557 flags = MAP_SHARED; 558 if (*addrp) 559 flags |= MAP_FIXED; 560 else 561 *addrp = (caddr_t)0x1000000; /* XXX */ 562 vn.v_type = VCHR; /* XXX */ 563 vn.v_specinfo = &si; /* XXX */ 564 vn.v_rdev = dev; /* XXX */ 565 error = uvm_mmap(&p->p_vmspace->vm_map, (vaddr_t *)addrp, 566 (vsize_t)len, VM_PROT_ALL, VM_PROT_ALL, 567 flags, (caddr_t)&vn, 0, 568 p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur); 569 if (error == 0) 570 (void) (*gp->g_sw->gd_mode)(gp, GM_MAP, *addrp); 571 return(error); 572 } 573 574 int 575 grfunmap(dev, addr, p) 576 dev_t dev; 577 caddr_t addr; 578 struct proc *p; 579 { 580 struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)]; 581 vsize_t size; 582 583 #ifdef DEBUG 584 if (grfdebug & GDB_MMAP) 585 printf("grfunmap(%d): dev %x addr %p\n", p->p_pid, dev, addr); 586 #endif 587 if (addr == 0) 588 return(EINVAL); /* XXX: how do we deal with this? */ 589 (void) (*gp->g_sw->gd_mode)(gp, GM_UNMAP, 0); 590 size = round_page(gp->g_display.gd_regsize + gp->g_display.gd_fbsize); 591 uvm_unmap(&p->p_vmspace->vm_map, (vaddr_t)addr, 592 (vaddr_t)addr + size); 593 return 0; 594 } 595 596 #ifdef COMPAT_HPUX 597 int 598 iommap(dev, addrp) 599 dev_t dev; 600 caddr_t *addrp; 601 { 602 603 #ifdef DEBUG 604 if (grfdebug & (GDB_MMAP|GDB_IOMAP)) 605 printf("iommap(%d): addr %p\n", curproc->p_pid, *addrp); 606 #endif 607 return(EINVAL); 608 } 609 610 int 611 iounmmap(dev, addr) 612 dev_t dev; 613 caddr_t addr; 614 { 615 #ifdef DEBUG 616 int unit = minor(dev); 617 618 if (grfdebug & (GDB_MMAP|GDB_IOMAP)) 619 printf("iounmmap(%d): id %d addr %p\n", 620 curproc->p_pid, unit, addr); 621 #endif 622 return(0); 623 } 624 625 /* 626 * Processes involved in framebuffer mapping via GCSLOT are recorded in 627 * an array of pids. The first element is used to record the last slot used 628 * (for faster lookups). The remaining elements record up to GRFMAXLCK-1 629 * process ids. Returns a slot number between 1 and GRFMAXLCK or 0 if no 630 * slot is available. 631 */ 632 int 633 grffindpid(gp) 634 struct grf_softc *gp; 635 { 636 register short pid, *sp; 637 register int i, limit; 638 int ni; 639 640 if (gp->g_pid == NULL) { 641 gp->g_pid = (short *) 642 malloc(GRFMAXLCK * sizeof(short), M_DEVBUF, M_WAITOK); 643 memset((caddr_t)gp->g_pid, 0, GRFMAXLCK * sizeof(short)); 644 } 645 pid = curproc->p_pid; 646 ni = limit = gp->g_pid[0]; 647 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) { 648 if (*sp == pid) 649 goto done; 650 if (*sp == 0) 651 ni = i; 652 } 653 i = ni; 654 if (i < limit) { 655 gp->g_pid[i] = pid; 656 goto done; 657 } 658 if (++i == GRFMAXLCK) 659 return(0); 660 gp->g_pid[0] = i; 661 gp->g_pid[i] = pid; 662 done: 663 #ifdef DEBUG 664 if (grfdebug & GDB_LOCK) 665 printf("grffindpid(%d): slot %d of %d\n", 666 pid, i, gp->g_pid[0]); 667 #endif 668 return(i); 669 } 670 671 void 672 grfrmpid(gp) 673 struct grf_softc *gp; 674 { 675 register short pid, *sp; 676 register int limit, i; 677 int mi; 678 679 if (gp->g_pid == NULL || (limit = gp->g_pid[0]) == 0) 680 return; 681 pid = curproc->p_pid; 682 limit = gp->g_pid[0]; 683 mi = 0; 684 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) { 685 if (*sp == pid) 686 *sp = 0; 687 else if (*sp) 688 mi = i; 689 } 690 i = mi; 691 if (i < limit) 692 gp->g_pid[0] = i; 693 #ifdef DEBUG 694 if (grfdebug & GDB_LOCK) 695 printf("grfrmpid(%d): slot %d of %d\n", 696 pid, sp-gp->g_pid, gp->g_pid[0]); 697 #endif 698 } 699 700 int 701 grflckmmap(dev, addrp) 702 dev_t dev; 703 caddr_t *addrp; 704 { 705 #ifdef DEBUG 706 struct proc *p = curproc; /* XXX */ 707 708 if (grfdebug & (GDB_MMAP|GDB_LOCK)) 709 printf("grflckmmap(%d): addr %p\n", 710 p->p_pid, *addrp); 711 #endif 712 return(EINVAL); 713 } 714 715 int 716 grflckunmmap(dev, addr) 717 dev_t dev; 718 caddr_t addr; 719 { 720 #ifdef DEBUG 721 int unit = minor(dev); 722 723 if (grfdebug & (GDB_MMAP|GDB_LOCK)) 724 printf("grflckunmmap(%d): id %d addr %p\n", 725 curproc->p_pid, unit, addr); 726 #endif 727 return(EINVAL); 728 } 729 #endif /* COMPAT_HPUX */ 730