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