1 /* $NetBSD: grf.c,v 1.4 1996/10/13 03:34:47 christos 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 <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/device.h> 54 #include <sys/proc.h> 55 #include <sys/ioctl.h> 56 #include <sys/file.h> 57 #include <sys/malloc.h> 58 #include <sys/vnode.h> 59 #include <sys/mman.h> 60 61 #include <x68k/dev/grfioctl.h> 62 #include <x68k/dev/grfvar.h> 63 #include <x68k/dev/itevar.h> 64 65 #include <machine/cpu.h> 66 67 #ifdef COMPAT_HPUX 68 #include <compat/hpux/hpux.h> 69 extern struct emul emul_hpux; 70 #endif 71 72 #include <vm/vm.h> 73 #include <vm/vm_kern.h> 74 #include <vm/vm_page.h> 75 #include <vm/vm_pager.h> 76 77 #include <miscfs/specfs/specdev.h> 78 79 #include "ite.h" 80 #if NITE == 0 81 #define iteon(u,f) 82 #define iteoff(u,f) 83 #endif 84 85 #ifdef DEBUG 86 int grfdebug = 0; 87 #define GDB_DEVNO 0x01 88 #define GDB_MMAP 0x02 89 #define GDB_IOMAP 0x04 90 #define GDB_LOCK 0x08 91 #endif 92 93 struct cfdriver grf_cd; 94 95 /*ARGSUSED*/ 96 int 97 grfopen(dev, flags) 98 dev_t dev; 99 int flags; 100 { 101 int unit = GRFUNIT(dev); 102 register struct grf_softc *gp = grf_cd.cd_devs[unit]; 103 int error = 0; 104 105 if (unit >= grf_cd.cd_ndevs || (gp->g_flags & GF_ALIVE) == 0) 106 return(ENXIO); 107 if ((gp->g_flags & (GF_OPEN|GF_EXCLUDE)) == (GF_OPEN|GF_EXCLUDE)) 108 return(EBUSY); 109 #ifdef COMPAT_HPUX 110 /* 111 * XXX: cannot handle both HPUX and BSD processes at the same time 112 */ 113 if (curproc->p_emul == &emul_hpux) 114 if (gp->g_flags & GF_BSDOPEN) 115 return(EBUSY); 116 else 117 gp->g_flags |= GF_HPUXOPEN; 118 else 119 if (gp->g_flags & GF_HPUXOPEN) 120 return(EBUSY); 121 else 122 gp->g_flags |= GF_BSDOPEN; 123 #endif 124 /* 125 * First open. 126 * XXX: always put in graphics mode. 127 */ 128 error = 0; 129 if ((gp->g_flags & GF_OPEN) == 0) { 130 gp->g_flags |= GF_OPEN; 131 error = grfon(dev); 132 } 133 return(error); 134 } 135 136 /*ARGSUSED*/ 137 int 138 grfclose(dev, flags) 139 dev_t dev; 140 int flags; 141 { 142 register struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)]; 143 144 (void) grfoff(dev); 145 #ifdef COMPAT_HPUX 146 (void) grfunlock(gp); 147 #endif 148 gp->g_flags &= GF_ALIVE; 149 return(0); 150 } 151 152 /*ARGSUSED*/ 153 int 154 grfioctl(dev, cmd, data, flag, p) 155 dev_t dev; 156 u_long cmd; 157 caddr_t data; 158 int flag; 159 struct proc *p; 160 { 161 int unit = GRFUNIT(dev); 162 register struct grf_softc *gp = grf_cd.cd_devs[unit]; 163 int error; 164 165 #ifdef COMPAT_HPUX 166 if (p->p_emul == &emul_hpux) 167 return(hpuxgrfioctl(dev, cmd, data, flag, p)); 168 #endif 169 error = 0; 170 switch (cmd) { 171 172 case GRFIOCGINFO: 173 bcopy((caddr_t)&gp->g_display, data, sizeof(struct grfinfo)); 174 break; 175 176 case GRFIOCON: 177 error = grfon(dev); 178 break; 179 180 case GRFIOCOFF: 181 error = grfoff(dev); 182 break; 183 184 case GRFIOCMAP: 185 error = grfmap(dev, (caddr_t *)data, p); 186 break; 187 188 case GRFIOCUNMAP: 189 error = grfunmap(dev, *(caddr_t *)data, p); 190 break; 191 192 case GRFSETVMODE: 193 error = (*gp->g_sw->gd_mode)(gp, GM_GRFSETVMODE, data); 194 if (error == 0) 195 ite_reinit(unit); 196 break; 197 198 default: 199 error = EINVAL; 200 break; 201 202 } 203 return(error); 204 } 205 206 /*ARGSUSED*/ 207 int 208 grfselect(dev, rw) 209 dev_t dev; 210 int rw; 211 { 212 if (rw == FREAD) 213 return(0); 214 return(1); 215 } 216 217 /*ARGSUSED*/ 218 int 219 grfmmap(dev, off, prot) 220 dev_t dev; 221 int off, prot; 222 { 223 return (grfaddr(grf_cd.cd_devs[GRFUNIT(dev)], off)); 224 } 225 226 int 227 grfon(dev) 228 dev_t dev; 229 { 230 int unit = GRFUNIT(dev); 231 struct grf_softc *gp = grf_cd.cd_devs[unit]; 232 233 /* 234 * XXX: iteoff call relies on devices being in same order 235 * as ITEs and the fact that iteoff only uses the minor part 236 * of the dev arg. 237 */ 238 iteoff(unit, 2); 239 return((*gp->g_sw->gd_mode)(gp, 240 (dev&GRFOVDEV) ? GM_GRFOVON : GM_GRFON, 241 (caddr_t)0)); 242 } 243 244 int 245 grfoff(dev) 246 dev_t dev; 247 { 248 int unit = GRFUNIT(dev); 249 struct grf_softc *gp = grf_cd.cd_devs[unit]; 250 int error; 251 252 (void) grfunmap(dev, (caddr_t)0, curproc); 253 error = (*gp->g_sw->gd_mode)(gp, 254 (dev&GRFOVDEV) ? GM_GRFOVOFF : GM_GRFOFF, 255 (caddr_t)0); 256 /* XXX: see comment for iteoff above */ 257 iteon(unit, 2); 258 return(error); 259 } 260 261 int 262 grfaddr(gp, off) 263 struct grf_softc *gp; 264 register int off; 265 { 266 register struct grfinfo *gi = &gp->g_display; 267 268 /* control registers */ 269 if (off >= 0 && off < gi->gd_regsize) 270 return(((u_int)gi->gd_regaddr + off) >> PGSHIFT); 271 272 /* frame buffer */ 273 if (off >= gi->gd_regsize && off < gi->gd_regsize+gi->gd_fbsize) { 274 off -= gi->gd_regsize; 275 return(((u_int)gi->gd_fbaddr + off) >> PGSHIFT); 276 } 277 /* bogus */ 278 return(-1); 279 } 280 281 /* 282 * HP-UX compatibility routines 283 */ 284 #ifdef COMPAT_HPUX 285 286 /*ARGSUSED*/ 287 hpuxgrfioctl(dev, cmd, data, flag, p) 288 dev_t dev; 289 u_long cmd; 290 caddr_t data; 291 int flag; 292 struct proc *p; 293 { 294 register struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)]; 295 int error; 296 297 error = 0; 298 switch (cmd) { 299 300 case GCID: 301 *(int *)data = gp->g_display.gd_id; 302 break; 303 304 case GCON: 305 error = grfon(dev); 306 break; 307 308 case GCOFF: 309 error = grfoff(dev); 310 break; 311 312 case GCLOCK: 313 error = grflock(gp, 1); 314 break; 315 316 case GCUNLOCK: 317 error = grfunlock(gp); 318 break; 319 320 case GCAON: 321 case GCAOFF: 322 break; 323 324 /* GCSTATIC is implied by our implementation */ 325 case GCSTATIC_CMAP: 326 case GCVARIABLE_CMAP: 327 break; 328 329 /* map in control regs and frame buffer */ 330 case GCMAP: 331 error = grfmap(dev, (caddr_t *)data, p); 332 break; 333 334 case GCUNMAP: 335 error = grfunmap(dev, *(caddr_t *)data, p); 336 /* XXX: HP-UX uses GCUNMAP to get rid of GCSLOT memory */ 337 if (error) 338 error = grflckunmmap(dev, *(caddr_t *)data); 339 break; 340 341 case GCSLOT: 342 { 343 struct grf_slot *sp = (struct grf_slot *)data; 344 345 sp->slot = grffindpid(gp); 346 if (sp->slot) { 347 error = grflckmmap(dev, (caddr_t *)&sp->addr); 348 if (error && gp->g_pid) { 349 free((caddr_t)gp->g_pid, M_DEVBUF); 350 gp->g_pid = NULL; 351 } 352 } else 353 error = EINVAL; /* XXX */ 354 break; 355 } 356 357 case GCDESCRIBE: 358 error = (*gp->g_sw->gd_mode)(gp, GM_DESCRIBE, data); 359 break; 360 361 /* 362 * XXX: only used right now to map in rbox control registers 363 * Will be replaced in the future with a real IOMAP interface. 364 */ 365 case IOMAPMAP: 366 error = iommap(dev, (caddr_t *)data); 367 #if 0 368 /* 369 * It may not be worth kludging this (using p_devtmp) to 370 * make this work. It was an undocumented side-effect 371 * in HP-UX that the mapped address was the return value 372 * of the ioctl. The only thing I remember that counted 373 * on this behavior was the rbox X10 server. 374 */ 375 if (!error) 376 u.u_r.r_val1 = *(int *)data; /* XXX: this sux */ 377 #endif 378 break; 379 380 case IOMAPUNMAP: 381 error = iounmmap(dev, *(caddr_t *)data); 382 break; 383 384 default: 385 error = EINVAL; 386 break; 387 } 388 return(error); 389 } 390 391 grflock(gp, block) 392 register struct grf_softc *gp; 393 int block; 394 { 395 struct proc *p = curproc; /* XXX */ 396 int error; 397 extern char devioc[]; 398 399 #ifdef DEBUG 400 if (grfdebug & GDB_LOCK) 401 printf("grflock(%d): dev %x flags %x lockpid %x\n", 402 p->p_pid, gp-grf_softc, gp->g_flags, 403 gp->g_lockp ? gp->g_lockp->p_pid : -1); 404 #endif 405 if (gp->g_pid) { 406 #ifdef DEBUG 407 if (grfdebug & GDB_LOCK) 408 printf(" lockpslot %d lockslot %d lock[lockslot] %d\n", 409 gp->g_lock->gl_lockslot, gp->g_lockpslot, 410 gp->g_lock->gl_locks[gp->g_lockpslot]); 411 #endif 412 gp->g_lock->gl_lockslot = 0; 413 if (gp->g_lock->gl_locks[gp->g_lockpslot] == 0) { 414 gp->g_lockp = NULL; 415 gp->g_lockpslot = 0; 416 } 417 } 418 if (gp->g_lockp) { 419 if (gp->g_lockp == p) 420 return(EBUSY); 421 if (!block) 422 return(OEAGAIN); 423 do { 424 gp->g_flags |= GF_WANTED; 425 if (error = tsleep((caddr_t)&gp->g_flags, 426 (PZERO+1) | PCATCH, devioc, 0)) 427 return (error); 428 } while (gp->g_lockp); 429 } 430 gp->g_lockp = p; 431 if (gp->g_pid) { 432 int slot = grffindpid(gp); 433 434 #ifdef DEBUG 435 if (grfdebug & GDB_LOCK) 436 printf(" slot %d\n", slot); 437 #endif 438 gp->g_lockpslot = gp->g_lock->gl_lockslot = slot; 439 gp->g_lock->gl_locks[slot] = 1; 440 } 441 return(0); 442 } 443 444 grfunlock(gp) 445 register struct grf_softc *gp; 446 { 447 #ifdef DEBUG 448 if (grfdebug & GDB_LOCK) 449 printf("grfunlock(%d): dev %x flags %x lockpid %d\n", 450 curproc->p_pid, gp-grf_softc, gp->g_flags, 451 gp->g_lockp ? gp->g_lockp->p_pid : -1); 452 #endif 453 if (gp->g_lockp != curproc) 454 return(EBUSY); 455 if (gp->g_pid) { 456 #ifdef DEBUG 457 if (grfdebug & GDB_LOCK) 458 printf(" lockpslot %d lockslot %d lock[lockslot] %d\n", 459 gp->g_lock->gl_lockslot, gp->g_lockpslot, 460 gp->g_lock->gl_locks[gp->g_lockpslot]); 461 #endif 462 gp->g_lock->gl_locks[gp->g_lockpslot] = 0; 463 gp->g_lockpslot = gp->g_lock->gl_lockslot = 0; 464 } 465 if (gp->g_flags & GF_WANTED) { 466 wakeup((caddr_t)&gp->g_flags); 467 gp->g_flags &= ~GF_WANTED; 468 } 469 gp->g_lockp = NULL; 470 return(0); 471 } 472 473 /* 474 * Convert a BSD style minor devno to HPUX style. 475 * We cannot just create HPUX style nodes as they require 24 bits 476 * of minor device number and we only have 8. 477 * XXX: This may give the wrong result for remote stats of other 478 * machines where device 10 exists. 479 */ 480 grfdevno(dev) 481 dev_t dev; 482 { 483 int unit = GRFUNIT(dev); 484 struct grf_softc *gp = grf_cd.cd_devs[unit]; 485 int newdev; 486 487 if (unit >= grf_cd.cd_ndevs || (gp->g_flags&GF_ALIVE) == 0) 488 return(bsdtohpuxdev(dev)); 489 /* magic major number */ 490 newdev = 12 << 24; 491 /* now construct minor number */ 492 if (gp->g_display.gd_regaddr != (caddr_t)GRFIADDR) { 493 int sc = patosc(gp->g_display.gd_regaddr); 494 newdev |= (sc << 16) | 0x200; 495 } 496 if (dev & GRFIMDEV) 497 newdev |= 0x02; 498 else if (dev & GRFOVDEV) 499 newdev |= 0x01; 500 #ifdef DEBUG 501 if (grfdebug & GDB_DEVNO) 502 printf("grfdevno: dev %x newdev %x\n", dev, newdev); 503 #endif 504 return(newdev); 505 } 506 507 #endif /* COMPAT_HPUX */ 508 509 int 510 grfmap(dev, addrp, p) 511 dev_t dev; 512 caddr_t *addrp; 513 struct proc *p; 514 { 515 struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)]; 516 int len, error; 517 struct vnode vn; 518 struct specinfo si; 519 int flags; 520 521 #ifdef DEBUG 522 if (grfdebug & GDB_MMAP) 523 printf("grfmap(%d): addr %p\n", p->p_pid, *addrp); 524 #endif 525 len = gp->g_display.gd_regsize + gp->g_display.gd_fbsize; 526 flags = MAP_SHARED; 527 if (*addrp) 528 flags |= MAP_FIXED; 529 else 530 *addrp = (caddr_t)0x1000000; /* XXX */ 531 vn.v_type = VCHR; /* XXX */ 532 vn.v_specinfo = &si; /* XXX */ 533 vn.v_rdev = dev; /* XXX */ 534 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp, 535 (vm_size_t)len, VM_PROT_ALL, VM_PROT_ALL, 536 flags, (caddr_t)&vn, 0); 537 if (error == 0) 538 (void) (*gp->g_sw->gd_mode)(gp, GM_MAP, *addrp); 539 return(error); 540 } 541 542 int 543 grfunmap(dev, addr, p) 544 dev_t dev; 545 caddr_t addr; 546 struct proc *p; 547 { 548 struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)]; 549 vm_size_t size; 550 int rv; 551 552 #ifdef DEBUG 553 if (grfdebug & GDB_MMAP) 554 printf("grfunmap(%d): dev %x addr %p\n", p->p_pid, dev, addr); 555 #endif 556 if (addr == 0) 557 return(EINVAL); /* XXX: how do we deal with this? */ 558 (void) (*gp->g_sw->gd_mode)(gp, GM_UNMAP, 0); 559 size = round_page(gp->g_display.gd_regsize + gp->g_display.gd_fbsize); 560 rv = vm_deallocate(&p->p_vmspace->vm_map, (vm_offset_t)addr, size); 561 return(rv == KERN_SUCCESS ? 0 : EINVAL); 562 } 563 564 #ifdef COMPAT_HPUX 565 iommap(dev, addrp) 566 dev_t dev; 567 caddr_t *addrp; 568 { 569 570 #ifdef DEBUG 571 if (grfdebug & (GDB_MMAP|GDB_IOMAP)) 572 printf("iommap(%d): addr %x\n", curproc->p_pid, *addrp); 573 #endif 574 return(EINVAL); 575 } 576 577 iounmmap(dev, addr) 578 dev_t dev; 579 caddr_t addr; 580 { 581 int unit = minor(dev); 582 583 #ifdef DEBUG 584 if (grfdebug & (GDB_MMAP|GDB_IOMAP)) 585 printf("iounmmap(%d): id %d addr %x\n", 586 curproc->p_pid, unit, addr); 587 #endif 588 return(0); 589 } 590 591 /* 592 * Processes involved in framebuffer mapping via GCSLOT are recorded in 593 * an array of pids. The first element is used to record the last slot used 594 * (for faster lookups). The remaining elements record up to GRFMAXLCK-1 595 * process ids. Returns a slot number between 1 and GRFMAXLCK or 0 if no 596 * slot is available. 597 */ 598 grffindpid(gp) 599 struct grf_softc *gp; 600 { 601 register short pid, *sp; 602 register int i, limit; 603 int ni; 604 605 if (gp->g_pid == NULL) { 606 gp->g_pid = (short *) 607 malloc(GRFMAXLCK * sizeof(short), M_DEVBUF, M_WAITOK); 608 bzero((caddr_t)gp->g_pid, GRFMAXLCK * sizeof(short)); 609 } 610 pid = curproc->p_pid; 611 ni = limit = gp->g_pid[0]; 612 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) { 613 if (*sp == pid) 614 goto done; 615 if (*sp == 0) 616 ni = i; 617 } 618 i = ni; 619 if (i < limit) { 620 gp->g_pid[i] = pid; 621 goto done; 622 } 623 if (++i == GRFMAXLCK) 624 return(0); 625 gp->g_pid[0] = i; 626 gp->g_pid[i] = pid; 627 done: 628 #ifdef DEBUG 629 if (grfdebug & GDB_LOCK) 630 printf("grffindpid(%d): slot %d of %d\n", 631 pid, i, gp->g_pid[0]); 632 #endif 633 return(i); 634 } 635 636 grfrmpid(gp) 637 struct grf_softc *gp; 638 { 639 register short pid, *sp; 640 register int limit, i; 641 int mi; 642 643 if (gp->g_pid == NULL || (limit = gp->g_pid[0]) == 0) 644 return; 645 pid = curproc->p_pid; 646 limit = gp->g_pid[0]; 647 mi = 0; 648 for (i = 1, sp = &gp->g_pid[1]; i <= limit; i++, sp++) { 649 if (*sp == pid) 650 *sp = 0; 651 else if (*sp) 652 mi = i; 653 } 654 i = mi; 655 if (i < limit) 656 gp->g_pid[0] = i; 657 #ifdef DEBUG 658 if (grfdebug & GDB_LOCK) 659 printf("grfrmpid(%d): slot %d of %d\n", 660 pid, sp-gp->g_pid, gp->g_pid[0]); 661 #endif 662 } 663 664 grflckmmap(dev, addrp) 665 dev_t dev; 666 caddr_t *addrp; 667 { 668 #ifdef DEBUG 669 struct proc *p = curproc; /* XXX */ 670 671 if (grfdebug & (GDB_MMAP|GDB_LOCK)) 672 printf("grflckmmap(%d): addr %x\n", 673 p->p_pid, *addrp); 674 #endif 675 return(EINVAL); 676 } 677 678 grflckunmmap(dev, addr) 679 dev_t dev; 680 caddr_t addr; 681 { 682 #ifdef DEBUG 683 int unit = minor(dev); 684 685 if (grfdebug & (GDB_MMAP|GDB_LOCK)) 686 printf("grflckunmmap(%d): id %d addr %x\n", 687 curproc->p_pid, unit, addr); 688 #endif 689 return(EINVAL); 690 } 691 #endif /* COMPAT_HPUX */ 692