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