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