1 /* $NetBSD: grf.c,v 1.19 2000/06/29 07:07:53 mrg 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 bcopy((caddr_t)&gp->g_display, data, 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 int rv; 583 584 #ifdef DEBUG 585 if (grfdebug & GDB_MMAP) 586 printf("grfunmap(%d): dev %x addr %p\n", p->p_pid, dev, addr); 587 #endif 588 if (addr == 0) 589 return(EINVAL); /* XXX: how do we deal with this? */ 590 (void) (*gp->g_sw->gd_mode)(gp, GM_UNMAP, 0); 591 size = round_page(gp->g_display.gd_regsize + gp->g_display.gd_fbsize); 592 rv = uvm_unmap(&p->p_vmspace->vm_map, (vaddr_t)addr, 593 (vaddr_t)addr + size); 594 return(rv == KERN_SUCCESS ? 0 : EINVAL); 595 } 596 597 #ifdef COMPAT_HPUX 598 int 599 iommap(dev, addrp) 600 dev_t dev; 601 caddr_t *addrp; 602 { 603 604 #ifdef DEBUG 605 if (grfdebug & (GDB_MMAP|GDB_IOMAP)) 606 printf("iommap(%d): addr %p\n", curproc->p_pid, *addrp); 607 #endif 608 return(EINVAL); 609 } 610 611 int 612 iounmmap(dev, addr) 613 dev_t dev; 614 caddr_t addr; 615 { 616 #ifdef DEBUG 617 int unit = minor(dev); 618 619 if (grfdebug & (GDB_MMAP|GDB_IOMAP)) 620 printf("iounmmap(%d): id %d addr %p\n", 621 curproc->p_pid, unit, addr); 622 #endif 623 return(0); 624 } 625 626 /* 627 * Processes involved in framebuffer mapping via GCSLOT are recorded in 628 * an array of pids. The first element is used to record the last slot used 629 * (for faster lookups). The remaining elements record up to GRFMAXLCK-1 630 * process ids. Returns a slot number between 1 and GRFMAXLCK or 0 if no 631 * slot is available. 632 */ 633 int 634 grffindpid(gp) 635 struct grf_softc *gp; 636 { 637 register short pid, *sp; 638 register int i, limit; 639 int ni; 640 641 if (gp->g_pid == NULL) { 642 gp->g_pid = (short *) 643 malloc(GRFMAXLCK * sizeof(short), M_DEVBUF, M_WAITOK); 644 bzero((caddr_t)gp->g_pid, GRFMAXLCK * sizeof(short)); 645 } 646 pid = curproc->p_pid; 647 ni = limit = gp->g_pid[0]; 648 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) { 649 if (*sp == pid) 650 goto done; 651 if (*sp == 0) 652 ni = i; 653 } 654 i = ni; 655 if (i < limit) { 656 gp->g_pid[i] = pid; 657 goto done; 658 } 659 if (++i == GRFMAXLCK) 660 return(0); 661 gp->g_pid[0] = i; 662 gp->g_pid[i] = pid; 663 done: 664 #ifdef DEBUG 665 if (grfdebug & GDB_LOCK) 666 printf("grffindpid(%d): slot %d of %d\n", 667 pid, i, gp->g_pid[0]); 668 #endif 669 return(i); 670 } 671 672 void 673 grfrmpid(gp) 674 struct grf_softc *gp; 675 { 676 register short pid, *sp; 677 register int limit, i; 678 int mi; 679 680 if (gp->g_pid == NULL || (limit = gp->g_pid[0]) == 0) 681 return; 682 pid = curproc->p_pid; 683 limit = gp->g_pid[0]; 684 mi = 0; 685 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) { 686 if (*sp == pid) 687 *sp = 0; 688 else if (*sp) 689 mi = i; 690 } 691 i = mi; 692 if (i < limit) 693 gp->g_pid[0] = i; 694 #ifdef DEBUG 695 if (grfdebug & GDB_LOCK) 696 printf("grfrmpid(%d): slot %d of %d\n", 697 pid, sp-gp->g_pid, gp->g_pid[0]); 698 #endif 699 } 700 701 int 702 grflckmmap(dev, addrp) 703 dev_t dev; 704 caddr_t *addrp; 705 { 706 #ifdef DEBUG 707 struct proc *p = curproc; /* XXX */ 708 709 if (grfdebug & (GDB_MMAP|GDB_LOCK)) 710 printf("grflckmmap(%d): addr %p\n", 711 p->p_pid, *addrp); 712 #endif 713 return(EINVAL); 714 } 715 716 int 717 grflckunmmap(dev, addr) 718 dev_t dev; 719 caddr_t addr; 720 { 721 #ifdef DEBUG 722 int unit = minor(dev); 723 724 if (grfdebug & (GDB_MMAP|GDB_LOCK)) 725 printf("grflckunmmap(%d): id %d addr %p\n", 726 curproc->p_pid, unit, addr); 727 #endif 728 return(EINVAL); 729 } 730 #endif /* COMPAT_HPUX */ 731