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